Node.js POST文件到服务器

jwe*_*ner 5 pipe node.js google-cloud-storage

我正在尝试编写一个应用程序,允许我的用户将文件上传到我的Google云端存储帐户.为了防止覆盖并在我身边做一些自定义处理和日志记录,我使用Node.js服务器作为上传的中间人.所以这个过程是:

  1. 用户将文件上载到Node.js服务器
  2. Node.js服务器解析文件,检查文件类型,在DB中存储一些数据
  3. Node.js服务器将文件上传到GCS
  4. Node.js服务器响应用户的请求,带有通过/失败备注

我在第3步有点迷失,确切地说是如何将该文件发送给GCS. 这个问题提供了一些有用的见解,以及一个很好的例子,但我仍然感到困惑.

据我所知,我可以打开一个ReadStream临时上传文件并将其传递给该http.request()对象.我感到困惑的是如何在POST请求中表示管道数据是file变量.根据GCS API文档,需要有一个file变量,它需要是最后一个变量.

那么,如何为管道数据指定POST变量名?

如果您可以告诉我如何直接从用户的上传中管道它,而不是将其存储在临时文件中,则可以获得奖励积分

Nat*_*dly 4

我相信如果你想做 POST,你必须使用Content-Type: multipart/form-data;boundary=myboundaryheader。然后,在正文中,write()每个字符串字段都是这样的(换行符应该是\r\n):

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

field_value
Run Code Online (Sandbox Code Playgroud)

然后对于文件本身,write()主体如下:

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

binary_file_data
Run Code Online (Sandbox Code Playgroud)

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");
});
Run Code Online (Sandbox Code Playgroud)

{end: false}会阻止pipe()自动关闭请求,因为您需要在发送完文件后再写入一个边界。--注意边界末端的额外内容。

最大的问题是 Google 可能需要content-length标头(很可能)。content-length如果是这种情况,那么您无法将用户的 POST 流式传输到 Google 的 POST,因为在收到整个文件之前,您无法可靠地知道该 POST 是什么。

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
Run Code Online (Sandbox Code Playgroud)

但是,这仍然会影响您从用户流式传输到谷歌的能力,必须将文件下载到本地磁盘才能确定其长度。

替代选项

...现在,经历了所有这些之后,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);
    }
}
Run Code Online (Sandbox Code Playgroud)