使用node.js流式传入传入请求

时间:2013-05-30 17:04:48

标签: node.js streaming

我正在使用带有express的node.js编写API。部分API将允许用户将大量二进制数据(可能数百MB)的有效负载存储在服务器数据库中。

现在,快速请求处理程序在整个上传准备就绪并且存储在服务器(req.body)的内存中之前不会被调用。然后它必须保存到数据库。有两件事我不喜欢这个。首先,它需要大量的服务器内存来同时保存所有二进制数据。第二个是像MongoDB和S3这样的许多数据库允许流式传输,因此在开始编写之前你并不需要掌握所有数据,因此没有理由等待。

所以我的问题是,可以将节点(通过快速或其他方式)配置为在整个请求进入之前开始流式传输到数据库吗?

1 个答案:

答案 0 :(得分:3)

经过进一步的研究,我发现原生的“http”模块确实支持我提到的方式的流媒体。我不确定express是否支持这一点。我猜它确实如此,但在上传的情况下你可能无法使用bodyParser中间件,因为这可能会阻塞,直到收到整个请求体。

无论如何,这里有一些代码显示如何将传入请求流式传输到MongoDB的GridFS:

var http = require('http');
var mongo = require('mongodb');

var db = new mongo.Db('somedb', new mongo.Server("localhost", 27017), { safe: true });

db.open(function(err) {
    if (err)
        console.log(err);

    http.createServer(function(req, res) {
        var numToSave = 0;
        var endCalled = false;

        new mongo.GridStore(db, new mongo.ObjectID(), "w", { root: "fs", filename: "test" }).open(function(err, gridStore) {
            if(err)
               console.log(err);

            gridStore.chunkSize = 1024 * 256;

            req.on("data", function(chunk) {
                numToSave++;

                gridStore.write(chunk, function(err, gridStore) {
                   if(err)
                      console.log(err);

                   numToSave--;

                   if(numToSave === 0 && endCalled)
                      finishUp(gridStore, res);
                });
            });

            req.on("end", function() {
                endCalled = true;
                console.log("end called");

                if(numToSave === 0)
                    finishUp(gridStore, res);
            });
        });
    }).listen(8000);
});

function finishUp(gridStore, res) {
    gridStore.close();
    res.end();
    console.log("finishing up");
}

要点是req对象实际上是一个包含“data”和“end”事件的流。每次发生“数据”事件时,都会向mongo写入一大块数据。发生“结束”事件时,关闭mongo连接并发送响应。

与协调所有不同的异步活动有关。在您有机会实际写出所有数据之前,您不希望关闭mongo连接。我用计数器和布尔值实现了这一点,但是使用某些库可能有更好的方法。