无法正确地将节点传送到下载文件

时间:2018-05-03 21:33:07

标签: node.js httpresponse node-streams

我对节点和流媒体相当新,我在尝试将大量数据流式传输到客户端浏览器上的文件时遇到问题。

因此,例如,如果在服务器上,如果我有一个大文件test.txt,我可以通过设置标题附件并将文件传递给请求响应,轻松地将其传输到客户端浏览器,如下所示。

res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-disposition', 'attachment;filename=myfile.text');

fs.createReadStream('./test.txt')
.pipe(res);

当用户点击该按钮时,下载开始,我们看到数据流式传输到下载文件。该流需要几分钟,但在此期间客户端不会被阻止,他们可以在浏览器下载文件时继续执行其他操作。

但是我的数据没有存储在文件中,我需要从另一台服务器一次检索一个字符串。所以我试图创建自己的读取流并按块推送我的数据块,但是当我做这样的事情时它不起作用:

var s = new Readable();
s.pipe(res);

for(let i=0; i<=total; i++) {
  dataString = //code here to get next string needed to push
  s.push(dataString);
};
s.push(null);

使用此代码,当用户请求下载时,一旦下载开始,客户端将被阻止,并且在下载完成之前无法执行任何其他操作。此外,如果数据流需要超过30秒,我们在这种情况下会达到服务器超时,并且下载失败。使用文件流这不是问题

如何让它像文件流一样运行,而不是阻止客户端在下载时执行其他请求。关于实施这一目标的最佳方式的任何建议都将受到赞赏。

1 个答案:

答案 0 :(得分:0)

我可以通过执行类似于此处的操作来解决此问题:  How to call an asynchronous function inside a node.js readable stream

我的基本代码如下,这不会阻止客户端或请求超时,因为数据会连续通过管道传输到客户端的文件下载。

res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-disposition', 'attachment;filename=myfile.text');

function MyStream() {
  var rs = new Readable();
  var hitsadded = 0;

  rs._read = function() {}; // needed to avoid "Not implemented" exception

  getResults(queryString, function getMoreUntilDone(err, res) {
      if (err){
        logger.logError(err);
      }

      rs.push(res.data);
      hitsadded += res.records;

      if (res.recordsTotal > hitsadded) {
        getNextPage(query, getMoreUntilDone);
      } else {
        rs.push(null);
      }
    });
  return rs;
}

 MyStream().pipe(zlib.createGzip()).pipe(res);