【问题标题】:Node.js POST File to ServerNode.js POST 文件到服务器
【发布时间】:2012-04-14 03:22:39
【问题描述】:

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

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

关于如何将该文件发送到 GCS 的第 3 步,我有点不知所措。 This question 提供了一些有用的见解,以及一个很好的例子,但我仍然感到困惑。

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

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

如果你能告诉我如何直接从我的用户上传的文件中进行管道传输,而不是将其存储在临时文件中,则可以加分

【问题讨论】:

    标签: node.js pipe google-cloud-storage


    【解决方案1】:

    我相信,如果你想做 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() 自动关闭请求,因为您需要在发送完文件后再写一个边界。请注意边界末尾的额外--

    最大的问题是 Google 可能需要 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
    

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

    备用选项

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

    【讨论】:

    • 不幸的是,谷歌要求它是一个 POST 请求才能做我想做的事情。但是,很好的答案,我一定会实施您提出的建议。
    • 另外,顺便说一下,node.js 让你能够stop pipe from closing the connection
    • 好点,我忘了。我将编辑答案以防其他人遇到它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-03
    • 2013-07-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多