简体   繁体   English

在 Express.js 中从 UInt8Array 发送二进制响应

[英]Send binary response from UInt8Array in Express.js

I am using Express.js with Typescript and I would like to send a UInt8Array as binary data.我将 Express.js 与 Typescript 一起使用,我想发送一个 UInt8Array 作为二进制数据。

This is what I use so far and it works, but I would like not to save the file before, because I think it wastes performance:这是我目前使用的并且有效,但我不想之前保存文件,因为我认为这会浪费性能:

const filePath = path.resolve(__dirname, 'template.docx');
const template = fs.readFileSync(filePath);
const buffer: Uint8Array = await createReport({
  template,
  data: {
    productCode: data.productCode,
  },
});
fs.writeFileSync(path.resolve(__dirname, 'output.docx'), buffer);
res.sendFile(path.resolve(__dirname, 'output.docx'));

I am using docx-templates to generate the file by the way.我正在使用 docx-templates 顺便生成文件。

You can use a PassThrough stream for this purpose, it'll keep the file in memory with no need to write to disk.为此,您可以使用PassThrough ZF7B44CFFAFD5C52223D5498196C8A2E7BZ,它将文件保存在 memory 中,无需写入磁盘。

Something like this should do it:这样的事情应该这样做:

    const stream = require("stream");
    const readStream = new stream.PassThrough();

    // Pass your output.docx buffer to this
    readStream.end(buffer);
    res.set("Content-disposition", 'attachment; filename=' + "output.docx");
    res.set("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
    readStream.pipe(res);

Terry,特里,

Thanks for the update of your answer and providing the full code.感谢您更新答案并提供完整代码。 However, it still does not help much.但是,它仍然没有太大帮助。 I am trying to understand how I can handle this on the front-end side, in my case in Vue.我试图了解如何在前端处理此问题,以我在 Vue 中的情况为例。 Here is the following code:这是以下代码:

router.post('/chart/word', async (req, res, next) => {
    try {
        if (!req.body.chartImage) throw new BadRequest('Missing the chart image from the request body')

        const wordTemplate = await s3GetFile('folder', 'chart-templates-export/charts-template.docx')
        const template = wordTemplate.Body

        const buffer = await createReport({
            cmdDelimiter: ["{", "}"],
            template,
            additionalJsContext: {
                chart: () => {
                    const dataUrl = req.body.chartImage.src
                    const data = dataUrl.slice("data:image/jpeg;base64,".length);
                    return { width: 18 , height: 12, data, extension: '.jpeg' }
                }
            }
        })

        const stream = require('stream')
        const readStream = new stream.PassThrough()

        readStream.end(buffer)
        res.set("Content-disposition", 'attachment; filename=' + "output.docx")
        res.set("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
        readStream.pipe(res)
    } catch (err) {
        console.log(err)
        next(err)
    }
})

And here is my Vue code, tested various stuff, but nothing...:这是我的 Vue 代码,测试了各种东西,但没有……:

async exportCharts() {
    console.log('this.$refs.test: ', this.$refs.test)
    let img = {
        src: this.$refs.test.getDataURL({
            type: 'jpeg',
            pixelRatio: window.devicePixelRatio || 1,
            backgroundColor: '#fff'
        }),
        width: this.$refs.test.getWidth(),
        height: this.$refs.test.getHeight()
    }
    const answersReq = await this.axios({
        method: 'post',
        url: '/pollAnswers/chart/word',
        data: {
            chartImage: img
        }
        responseType: 'arraybuffer' // 'blob' // 'document'
    })

    console.log('answersReq: ', answersReq)

    if (answersReq.data) {
        downloadURL(answersReq.data, 'report.docx')
    }
}

What I am basically doing is: sending an image to the API (taken from html vue-echart element), then inserting it in a docx template, by using docx-templates library, which returns me Uint8Array that I want to export as the new Word Document with the populated charts.我基本上在做的是:将图像发送到 API(取自 html vue-echart 元素),然后使用 docx-templates 库将其插入到 docx 模板中,这会返回我想要导出为新的 Uint8Array带有填充图表的 Word 文档。 Then, the user (on the UI) should be able to choose the destination.然后,用户(在 UI 上)应该能够选择目的地。

Here is the code for the download URL:下面是下载URL的代码:

export function downloadURL(data, fileName) {
    const mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    const blob = new Blob([data], { type: mimeType })
    const url = URL.createObjectURL(blob)

    const element = document.createElement('a')

    element.href = url
    element.download = fileName

    element.style.display = 'none'

    document.body.appendChild(element)
    element.click()
    URL.revokeObjectURL(element.href)
    document.body.removeChild(element)
}

PS Just to mention, if I directly save the buffer (the Uint8Array returned from the createReport) in the API, it works, the file is downloaded successfully and I can read it without any problems - it populates the correct chart in the file. PS 顺便提一下,如果我直接将缓冲区(从 createReport 返回的 Uint8Array)保存在 API 中,它会工作,文件已成功下载,我可以毫无问题地读取它 - 它会在文件中填充正确的图表。

UPDATE: I figured that out, but I am not sure why this is necessary and why it works that way and not the other.更新:我想通了,但我不确定为什么这是必要的以及为什么它以这种方式而不是另一种方式工作。 So, in the /chart/word endpoint, I am converting the Uint8Array buffer into a stream, then passing it as a response (the same way you used).因此,在/chart/word端点中,我将Uint8Array buffer转换为 stream,然后将其作为响应传递(与您使用的方式相同)。 Afterwards, in the Vue, I fetched this as responseType: 'arraybuffer' , which converted the stream response into Uint8Array buffer again, then, I used the same method for the download and it works.之后,在 Vue 中,我将其作为responseType: 'arraybuffer'获取,它再次将 stream 响应转换为Uint8Array buffer ,然后,我使用相同的方法进行下载并且它有效。 Initially, I tried to send directly the buffer (without converting it as stream as you mentioned), but then on the front-end, the response was received as object that contained the Uint8Array buffer values, which was not what is expected and I could not create legit docx file.最初,我尝试直接发送缓冲区(没有像你提到的那样将其转换为 stream),但随后在前端收到的响应为 object,其中包含Uint8Array buffer值,这不是预期的,我可以不创建合法的 docx 文件。 So, for some reason, it is required to convert the buffer as stream in the API, before sending it as response.因此,出于某种原因,需要将缓冲区转换为 API 中的 stream,然后再将其作为响应发送。 Afterwards, on the front-end, I have to convert it back to arraybuffer and, finally, to make the docx download.之后,在前端,我必须将它转换回 arraybuffer,最后,下载 docx。

If you can explain to me why it works like that, I will be very happy.如果你能向我解释为什么它会这样工作,我会很高兴。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM