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

fkaflof6  于 2022-11-18  发布在  TypeScript
关注(0)|答案(2)|浏览(123)

我正在使用Express.js和Typescript,我希望以二进制数据形式发送UInt 8Array。
到目前为止,我使用的是这个文件,它可以正常工作,但我不想在此之前保存该文件,因为我认为这会浪费性能:

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'));

我使用docx-templates来生成文件。

yiytaume

yiytaume1#

您可以使用PassThrough流来实现此目的,它会将文件保存在内存中,而无需写入磁盘。
这样就可以了:

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);

完整的node.js代码:

const fs = require("fs");
const express = require("express");
const port = 8000;
const app = express();
const stream = require("stream");

app.get('/download-file', (req, res) => {
    const buffer = fs.readFileSync("./test.docx");
    console.log("/download-file: Buffer length:", buffer.length);
    
    const readStream = new stream.PassThrough();
    readStream.end(buffer);
    res.set("Content-disposition", 'attachment; filename=' + "test.docx");
    res.set("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
    readStream.pipe(res);
});

app.listen(port);
console.log(`Serving at http://localhost:${port}`);

要进行测试,请将“test.docx”文件添加到同一目录,然后将浏览器指向http://localhost:8000/download-file

cngwdvgl

cngwdvgl2#

泰瑞,
感谢您更新答案并提供完整的代码。但是,它仍然没有太大帮助。我试图了解如何在前端处理这个问题,在我的Vue中。下面是以下代码:

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)
    }
})

这是我的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')
    }
}

我基本上在做的是:发送一个图像到API(从html vue-echart元素中获取),然后使用docx-templates库将其插入到docx模板中,该库返回Uint 8Array,我希望将其导出为带有填充图表的新Word文档。然后,用户(在UI上)应该能够选择目标。
下面是下载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)
}

P.S.仅提一下,如果我直接在API中保存缓冲区(从createReport返回的Uint 8Array),它就可以工作,文件下载成功,我可以毫无问题地读取它-它会在文件中填充正确的图表。

**UPDATE:**我知道了,但是我不确定为什么这样做是必要的,为什么这样做是正确的。所以,在/chart/word端点,我把Uint8Array buffer转换成一个流,然后把它作为一个响应传递(和你使用的方法一样)。然后,在Vue中,我把它作为responseType: 'arraybuffer'获取,它把流响应再次转换成Uint8Array buffer,然后,我用同样的方法下载,它工作了。最初,我试图直接发送缓冲区(没有像你提到的那样把它转换成流),但是在前端,响应被作为包含Uint8Array buffer值的对象接收,这不是预期的,我不能创建合法的docx文件。所以,出于某种原因,在API中,在把它作为响应发送之前,需要把缓冲区转换成

  • 如果你能告诉我为什么会这样,我会很高兴 *

相关问题