0

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

就目前而言,在整个上传准备好并存储在服务器(req.body)的内存中之前,不会调用快速请求处理程序。然后必须将其保存到数据库中。我不喜欢这件事有两点。首先是它需要大量的服务器内存来一次保存所有二进制数据。第二个是 MongoDB 和 S3 等许多数据库都允许流式传输,因此在开始编写之前您实际上并不需要将所有数据都准备好,因此没有理由等待。

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

4

1 回答 1

3

经过进一步研究,我发现原生“http”模块确实支持我提到的流式传输。我不确定快递是否支持这一点。我猜是这样,但在上传的情况下,您可能无法使用 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 对象实际上是一个带有“数据”和“结束”事件的流。每次发生“数据”事件时,您都会将一大块数据写入 mongo。当“结束”事件发生时,您关闭 mongo 连接并发送响应。

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

于 2013-06-13T21:23:09.683 回答