Node.js POST 文件到服务器

标签 node.js pipe google-cloud-storage

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

  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 变量名称?

如果你能告诉我如何直接从我的用户上传的文件中传输它,而不是将它存储在一个临时文件中,我将加分

最佳答案

我相信如果你想做 POST,你必须使用 Content-Type: multipart/form-data;boundary=myboundary header 。然后,在正文中,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() 自动关闭请求,因为您需要在发送完文件后再写入一个边界。请注意边界末端的额外 --

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

content-length header 的值应该是整个正文的单个数字。执行此操作的简单方法是在整个主体上调用 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

但是,这仍然会破坏您从用户流式传输到 google 的能力,文件必须下载到本地磁盘以确定其长度。

备选方案

...现在,在经历了所有这些之后,PUT 可能是你的 friend 。根据https://developers.google.com/storage/docs/reference-methods#putobject您可以使用 transfer-encoding: chunked header ,这样您就不需要查找文件长度。而且,我相信请求的整个主体只是文件,因此您可以使用 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);
    }
}

关于Node.js POST 文件到服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9943010/

相关文章:

Golang 使用 Run() 执行的命令没有返回

java - 尝试从 java 运行 gsutils 永远不会返回

android - 使用 JSON API 将文件上传到 Google Cloud Storage,错误 401 Unauthorized

python - 正确链接 Popen 子进程

node.js - FireBase 错误解析错误 : Unexpected token return

c++ - 如何在 nodejs 中获取多个 C++ 插件实例?

javascript - Node : Wrapping entire script in a function call

具有多个管道的 Python 子进程

python - Google Cloud Endpoints 可以与文本转语音配合使用吗?

javascript - 无法读取 Node js中传入请求的数据