在Node.js的Express中如何分部发送数据

13

我在搜索有关node.js express模块的网络和文档,并发现似乎没有办法分段发送数据。我有一个文件渲染得不太快,我想在所有内容都被渲染之前就发送它的部分。

所以这里是我的问题:

  1. response中是否有一种方法可以按部分发送数据?
  2. response.end()是什么意思?
  3. 如果没有办法分段发送数据 - 其背后的原理是什么?我会说如果这是真的,看起来比非阻塞更具阻塞性。如果提前给出数据,浏览器可以更快地加载信息。

简化示例代码:

app.get(..) {
  renderFile(file, function(data) {
    response.send(data);
  });
  response.end();
)

这段代码只发送了第一个数据块。我已经检查过 - 数据确实被正确地给出,回调函数也被调用了不止一次。

当然,我可以将数据附加到一个变量中,然后编写 response.send(data);,但我不喜欢这种方法 - 这不是它应该工作的方式。

5个回答

25

response对象是一个可写流。只需向其写入内容,Express会自动为您设置分块编码。

response.write(chunk);

如果产生这个“文件”的任何内容以可读流的方式呈现,你也可以将其导入其中。 http://nodejs.org/api/stream.html


1
请详细说明管道并举例说明。我认为这对很多人来说非常相关,因为在许多情况下,它实际上是更好的解决方案。 - Niels Abildgaard
@NielsAbildgaard 我无法给出具体的示例,因为问题中的代码没有提供数据来自何处的任何上下文。sourceStream.pipe(response) 将解决问题。 - Brad

2

简单的方法是将数据转换为可读流并将其管道传输到响应对象。这是我的Node.js代码。希望这可以帮助。

var express= require('express');
var bodyParser = require('body-parser');
const JSONStreamStringify = require('json-stream-stringify');

var app=express();
var jsonParser = bodyParser.json();

app.post('/node_test_pipe', jsonParser, function (req, res) {
    console.log("Request from:"+req.url);
    var obj={
             my:1,
             two:2
            }
    JSONStreamStringify(obj).pipe(res);
    var obj={
             my:3,
             two:4
            }
    JSONStreamStringify(obj).pipe(res);
    });

app.listen(8080);

2
除了使用Response.write()方法来分块发送和Response.end()来标记结束之外!如果您有一个流!您可以直接将其管道响应对象!所有内容都会自动处理!请参见下面的示例,其中包含可读文件流:
var http = require('http');
var fs = require('fs');

http.createServer(function(req, res) {
  // The filename is simple the local directory and tacks on the requested url
  var filename = __dirname+req.url;

  // This line opens the file as a readable stream
  var readStream = fs.createReadStream(filename);

  // This will wait until we know the readable stream is actually valid before piping
  readStream.on('open', function () {
    // This just pipes the read stream to the response object (which goes to the client)
    readStream.pipe(res); // <====== here
  });

  // This catches any errors that happen while creating the readable stream (usually invalid names)
  readStream.on('error', function(err) {
    res.end(err);
  });
}).listen(8080);

你可以使用任何数据流来实现这个功能。只需要将其管道连接到响应对象即可!上面的示例来自于这里:

https://nodejs.org/en/knowledge/advanced/streams/how-to-use-fs-create-read-stream/

底线

使用管道!这样看起来更好,也更易于理解!

这是文档的链接!

https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options

可读流的`readable.pipe()`方法会将一个`Writable`流附加到可读流上,从而自动切换为流动模式并将所有数据推送到所附加的`Writable`流中。
需要注意的是,可以将多个`Writable`流附加到单个可读流上。`readable.pipe()`方法返回对目标流的引用,这使得可以设置管道流链。
例如:
const fs = require('fs');
const r = fs.createReadStream('file.txt'); // read stream
const z = zlib.createGzip(); // write stream
const w = fs.createWriteStream('file.txt.gz'); // write stream
r.pipe(z).pipe(w);


阅读文档以获取更多信息!

1
Express扩展了Connect,而我相信Connect又扩展了HTTP。查看HTTP API,似乎你要找的是response.write,它可以将数据块作为响应体发送。然后,我认为你可以在结束信号上使用response.end

-1
响应对象可以被写入。在您的情况下,以下是一个解决方案:
renderFile(file, function(data) {
  response.send(data);
});
response.end();

如果您有机会将文件作为流获取,则可以进一步优化此过程:
file.getReadableStream().pipe(response);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接