2013-05-30 58 views
0

我正在使用node.js使用express來編寫API。部分API將允許用戶將大量的二進制數據有效載荷(可能是數百MB)存儲在服務器數據庫中。使用node.js流式傳入請求

現在看來,直到整個上傳準備好並存儲在服務器上的內存中(req.body),纔會調用express請求處理程序。然後它必須保存到數據庫。有兩件事我不喜歡這件事。首先,它需要大量的服務器內存來同時保存所有的二進制數據。其次,很多像MongoDB和S3這樣的數據庫都允許流式傳輸,因此在開始編寫數據之前並不需要將所有數據都存在,因此沒有理由等待。

所以我的問題是,節點(通過Express或其他方式)可以配置爲在整個請求進入之前開始流式傳輸到數據庫嗎?

+0

請谷歌搜索「節點流上傳」。 –

+0

我先試了一下。所有的例子似乎都是這樣的一些衍生物:http://debuggable.com/posts/streaming-file-uploads-with-node-js:4ac094b2-b6c8-4a7f-bd07-28accbdd56cb,這是如此古老,它不會在當前節點構建上運行。節點服務器如何知道在收到整個主體之前調用處理程序?它檢查內容類型(多部分)嗎?我寧願不要客戶端使用多部分POST發出特殊請求。如果他們只需定期發佈POST並讓節點立即啓動,那將會更好。 – d512

回答

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連接。我用計數器和布爾值實現了這一點,但是使用某個庫可能會有更好的方法。