2012-03-30 69 views
5

我正在嘗試編寫一個應用程序,允許我的用戶將文件上傳到我的Google Cloud Storage帳戶。爲了防止覆蓋並做一些自定義處理和日誌記錄,我使用Node.js服務器作爲上傳的中間人。所以這個過程是:Node.js POST文件到服務器

  1. 用戶上傳文件到服務器的Node.js
  2. 的Node.js服務器解析文件,檢查文件類型,存儲在數據庫的一些數據
  3. 的Node.js服務器上傳文件到GCS
  4. 的Node.js服務器響應用戶的使用要求通過/失敗的話

我開始有點失落的步驟3中,究竟是如何將該文件發送到GCS。 This question提供了一些有用的見解,以及一個很好的例子,但我仍然感到困惑。

據我所知,我可以爲臨時上傳文件打開一個ReadStream,並將其傳遞給http.request()對象。我感到困惑的是,如何在我的POST請求中表示管道數據是變量file。根據GCS API Docs,需要有一個file變量,它需要是最後一個。

所以,我怎麼對管道數據指定POST變量名?如果

加分,你能告訴我怎麼管直接從我的用戶的上傳,而不是將其存儲在一個臨時文件

回答

4

我相信,如果你想要做的POST,您必須使用Content-Type: multipart/form-data;boundary=myboundary標題。然後,在身體,write()這樣的事情每串場(換行符應該是\r\n):

--myboundary 
Content-Disposition: form-data; name="field_name" 

field_value 

,然後對文件本身,write()像這樣對身體:

--myboundary 
Content-Disposition: form-data; name="file"; filename="urlencoded_filename.jpg" 
Content-Type: image/jpeg 
Content-Transfer-Encoding: binary 

binary_file_data 

binary_file_data是在您使用pipe()

var fileStream = fs.createReadStream("path/to/my/file.jpg"); 
fileStream.pipe(requestToGoogle, {end: false}); 
fileStream.on('end, function() { 
    req.end("--myboundary--\r\n\r\n"); 
}); 

{end: false}防止pipe()不會自動關閉請求,因爲在完成發送文件後您需要再寫一個邊界。請注意邊界末端的額外--

大疑難雜症是,谷歌可能需要content-length頭(很可能)。如果是這種情況,那麼您無法將用戶的POST流式傳輸到Google發佈的POST,因爲在您收到整個文件之前,您無法可靠地知道content-length是什麼。

content-length標頭的值應該是整個身體的單個數字。要做到這一點的簡單方法是在整個機構上撥打Buffer.byteLength(body),但如果您有大型文件,它會很快變得醜陋,並且也會導致流式傳輸。另一種方法是,計算它像這樣:

var body_before_file = "..."; // string fields + boundary and metadata for the file 
var body_after_file = "--myboundary--\r\n\r\n"; 
var fs = require('fs'); 
fs.stat(local_path_to_file, function(err, file_info) { 
    var content_length = Buffer.byteLength(body_before_file) + 
      file_info.size + 
      Buffer.byteLength(body_after_file); 
    // create request to google, write content-length and other headers 
    // write() the body_before_file part, 
    // and then pipe the file and end the request like we did above 

但是,仍然殺死你從用戶到流式傳輸到谷歌的能力,該文件已被下載到本地磁盤,以確定它的長度。

備用選項

...現在,經過了這一切後去,PUT可能會在這裏你的朋友。根據https://developers.google.com/storage/docs/reference-methods#putobject,您可以使用transfer-encoding: chunked標題,因此不需要查找文件長度。而且,我相信請求的全部內容僅僅是文件,因此您可以使用pipe(),並在請求完成時讓它結束請求。如果您使用https://github.com/felixge/node-formidable來處理上傳,那麼你可以做這樣的事情:

incomingForm.onPart = function(part) { 
    if (part.filename) { 
     var req = ... // create a PUT request to google and set the headers 
     part.pipe(req); 
    } else { 
     // let formidable handle all non-file parts 
     incomingForm.handlePart(part); 
    } 
} 
+0

不幸的是,谷歌需要它是爲了做什麼,我試圖做一個POST請求。然而,偉大的答案,我一定會實施你提出的建議。 – jwegner 2012-04-02 13:10:52

+0

另外,順便說一下,node.js讓你能夠[關閉連接管道](http://nodejs.org/api/stream.html#stream_stream_pipe_destination_options) – jwegner 2012-04-02 20:31:40

+0

好點,我忘記了這一點。我會編輯答案,以防其他人遇到它。 – 2012-04-02 21:42:06