如何以快递方式流式传输响应?

Posted

技术标签:

【中文标题】如何以快递方式流式传输响应?【英文标题】:How do I stream response in express? 【发布时间】:2016-12-11 20:08:05 【问题描述】:

我一直在尝试让一个快速应用程序以流的形式发送响应。

var Readable = require('stream').Readable;
var rs = Readable();


app.get('/report', function(req,res) 
    
    res.statusCode = 200;
    res.setHeader('Content-type', 'application/csv');
    res.setHeader('Access-Control-Allow-Origin', '*');

    // Header to force download
    res.setHeader('Content-disposition', 'attachment; filename=Report.csv');

    
    rs.pipe(res);

    rs.push("USERID,NAME,FBID,ACCOUNT,SUBSCRIPTION,PRICE,STATE,TIMEPERIOD\n");

    for (var i = 0; i < 10; i++) 
        rs.push("23,John Doe,1234,500,SUBSCRIPITON,100,ACTIVE,30\n");
    

    rs.push(null);
);      

当我将“rs.pipe(res)”替换为“rs.pipe(process.stdout)”时,它会在控制台中打印。但是如何让它在快递应用中运行呢?

Error: not implemented
    at Readable._read (_stream_readable.js:465:22)
    at Readable.read (_stream_readable.js:341:10)
    at Readable.on (_stream_readable.js:720:14)
    at Readable.pipe (_stream_readable.js:575:10)
    at line "rs.pipe(res);"

【问题讨论】:

你需要继承Readable,它需要有一个_read方法。但为什么不直接使用res.write(...) 我将如何在这里使用 res.write()? 【参考方案1】:

您不需要可读的流实例,只需使用res.write()

res.write("USERID,NAME,FBID,ACCOUNT,SUBSCRIPTION,PRICE,STATE,TIMEPERIOD\n");

for (var i = 0; i < 10; i++) 
    res.write("23,John Doe,1234,500,SUBSCRIPITON,100,ACTIVE,30\n");


res.end();

这是因为在 Express 中,res 是基于 Node 自己的 http.serverResponse,所以它继承了它的所有方法(如 write)。

【讨论】:

如果我在响应中添加数千行,这仍然不是问题吗? 不,因为res也是一个流。 谢谢。我整天都在尝试使用可读流来做到这一点。 这不会流式传输,缓冲直到 res.end() 被调用 @chetandev 您的浏览器/客户端可能会缓冲它,但 Express 或 http 不会。您可以使用像nc/netcat 这样的非缓冲 TCP 客户端对此进行测试。见this gist。【参考方案2】:

我能够让它工作。

将此代码放入您的 Express 路由器中。 打开网络浏览器 转到http://yourdomain/yourrouterpath/stream

...

router.get('/stream', function (req, res, next) 
  //when using text/plain it did not stream
  //without charset=utf-8, it only worked in Chrome, not Firefox
  res.setHeader('Content-Type', 'text/html; charset=utf-8');
  res.setHeader('Transfer-Encoding', 'chunked');

  res.write("Thinking...");
  sendAndSleep(res, 1);
);


var sendAndSleep = function (response, counter) 
  if (counter > 10) 
    response.end();
   else 
    response.write(" ;i=" + counter);
    counter++;
    setTimeout(function () 
      sendAndSleep(response, counter);
    , 1000)
  ;
;

【讨论】:

【参考方案3】:

为了使用 tar-stream,我需要以 express 流式传输响应。以下是我的做法,以防它对任何人有所帮助。

请求来自存储在服务器上的 tar 文件中的单个文件。

const fs = require("fs"),
   tar = require("tar-stream");

app.get("/fileFromTar/*", (req, res) => 
   const fileWanted = req.params[0],
      readStream = fs.createReadStream('myTarFile.tar'),
      extractor = tar.extract();

   extractor.on('entry', (header, stream, next) => 
      stream.on('end', next);

      if (header.name === fileWanted) 
         const  size  = header;
         res.set(
           "Content-Type": 'audio/flac', // or whichever one applies
           "Content-Length": size,
           "Content-Range": `bytes 0-$size/$size`
         );
         stream.pipe(res);
      
      else stream.resume();
   );
   readStream.pipe(extractor);
);

【讨论】:

以上是关于如何以快递方式流式传输响应?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 servlet 将文本响应流式传输到 jsp?

如何以编程方式使用 AWS Media Convert 流式传输视频

Groovy Grails,如何在控制器的响应中流式传输或缓冲大文件?

如何模拟通话以将蓝牙音频流式传输到车辆中?

如何通过烧瓶应用程序流式传输数据?

流式 REST API