【问题标题】:ExpressJS - Generate a CSV file on the server side and enable download from the client sideExpressJS - 在服务器端生成 CSV 文件并启用从客户端下载
【发布时间】:2019-08-23 07:40:30
【问题描述】:

我正在使用 ExpressJS 开发一个网络应用程序。

页面上有一个创建按钮。我想要实现的是,当按钮被点击时,它会向服务器端发送一个 Post/Get 请求,然后服务器端会触发一个进程来生成一个 CSV 文件并将其发送回客户端进行下载。

我正在考虑使用json2csv

客户端:

$.ajax({
            type: "POST",
            url: "/incidents/createTable",
            success: function() {
                // HOW TO GET THE RETURNED DATA TO A DOWNLOADABLE FILE?
            }
        });

服务器端和incidents路由器(后面的sn-p代码是从json2csv官方npmjs页面复制过来的):

const { AsyncParser } = require('json2csv');
// THIS FUNCTION SHOULD GENERATE A CSV FILE IN MEMORY
router.post("/createTable", async function(req, res, next) {
    console.log("flag 1");  // For flagging

    const fields = ['field1', 'field2', 'field3'];
    const opts = { fields };
    const transformOpts = { highWaterMark: 8192 };

    const asyncParser = new AsyncParser(opts, transformOpts);

    console.log("flag 2");  // For flagging
    let csv = '';
    asyncParser.processor
    .on('data', chunk => (csv += chunk.toString()))
    .on('end', () => res.send(csv))
    .on('error', err => console.error(err));
});

当我运行 Web 应用程序并单击“创建”按钮时,服务器挂在那里,它通过了“标志 2”并且从未通过 asyncParser.processor。在客户端,POST 请求也挂在那里,没有状态码。

【问题讨论】:

  • 你的意思是'flag 1'被记录了但没有'flag 2'?
  • 抱歉有错别字。它记录了flag 2,但没有记录asyncParser.processor
  • 好的,但是您将哪些数据提供给处理器?
  • 代码看起来不完整,您没有向处理器提供任何数据。在您复制代码的文档上,下一行应为 asyncParser.input.push(data); // This data might come from an HTTP request, etc. 但您没有相应的行
  • 您可以添加res.attachment('data.csv') 以使浏览器下载文件,但要使其正常工作,您不能使用ajax。你必须提交<form> 另一个选项是使用FileSaver.jsStreamSaver 之类的东西。您还可以在客户端生成 csv 并触发下载

标签: javascript node.js express json2csv


【解决方案1】:

经过大量挖掘,终于找到了解决方案。

服务器端:

var stream = require('stream');
//...
router.post("/createTable", async function(req, res, next) {
    var fileContents = Buffer.from(JSON.stringify({
        sampleTime: '1450632410296',
        sampleData: '1234567890'
    }));

    var readStream = new stream.PassThrough();
    readStream.end(fileContents);

    res.set('Content-disposition', 'attachment; filename=' + "download.csv");
    res.set('Content-Type', 'text/csv');

    readStream.pipe(res);
});

客户端:

 $.ajax({
        type: "POST",
        url: "/incidents/createTable",
        success: function(result) {
            var blob=new Blob([result], {type: 'text/csv'});
            var link=document.createElement('a');

            link.style = "display: none";
            document.body.appendChild(link);

            var url = window.URL.createObjectURL(blob);
            link.href = url;
            console.log(url);
            link.download="download.csv";
            link.click();
            window.URL.revokeObjectURL(url);
        }
    });

【讨论】:

    【解决方案2】:

    您缺少一个部分,即提供数据。在您这样做之前,解析器不会做任何事情。这也是挂断请求,因为永远无法到达res.send

    来自文档:

    asyncParser.input.push(data); // This data might come from an HTTP request, etc.
    asyncParser.input.push(null); // Sending `null` to a stream signal that no more data is expected and ends it.
    

    这是将产生的完整代码

    "field1","field2","field3"
    1,2,3
    

    在 GET /createTable 上

    const { AsyncParser } = require('json2csv');
    const express = require('express');
    
    const app = express();
    
    app.get("/createTable", async function(req, res, next) {
        console.log("flag 1");  // For flagging
    
        const fields = ['field1', 'field2', 'field3'];
        const opts = { fields };
        const transformOpts = { highWaterMark: 8192 };
    
        const asyncParser = new AsyncParser(opts, transformOpts);
    
        console.log("flag 2");  // For flagging
        let csv = '';
        asyncParser.processor
          .on('data', chunk => (csv += chunk.toString()))
          .on('end', () => res.send(csv))
          .on('error', err => console.error(err));
    
        asyncParser.input.push('{ "field1": 1, "field2": 2, "field3": 3 }');
        asyncParser.input.push(null); // Sending `null` to a stream signal that no more data is expected and ends it.
    });
    
    app.listen(3000);
    

    【讨论】:

    • 谢谢。我试过你的代码。客户端确实打印出"field1","field2","field3" 1,2,3。但这不是我想要达到的。我希望将输出作为 csv 文件供用户下载,如主题标题中所述。
    • 有很多方法可以做到这一点。 stackoverflow.com/questions/13405129/…
    猜你喜欢
    • 2016-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 2017-02-19
    相关资源
    最近更新 更多