【问题标题】:"Header content contains invalid characters" error when piping multipart upload part into a new request将分段上传部分传送到新请求时出现“标头内容包含无效字符”错误
【发布时间】:2022-02-22 15:38:32
【问题描述】:

我的快速服务器接收来自浏览器的文件上传。上传以multipart/form-data 请求传输;我使用多方来解析传入的实体主体。

Multiparty 允许您将 part(大致是像<input type="file"> 这样的单个表单字段)作为可读流。我不想在我的网络服务器上处理或存储上传的文件,所以我只是将上传的文件部分通过管道发送到另一个服务的请求中(使用请求模块)。

app.post('/upload', function(req, res) {
    var form = new multiparty.Form();

    form.on('part', function(part) {

        var serviceRequest = request({
            method: 'POST',
            url: 'http://other-service/process-file',
            headers: {
                'Content-Type': 'application/octet-stream'
            }
        }, function(err, svcres, body) {
            // handle response
        });

        part.pipe(serviceRequest);
    });

    form.parse(req);
});

这在大多数情况下都能正常工作。节点自动应用分块传输编码,并且随着浏览器上传文件字节,它们被正确地作为原始实体体(没有多部分格式)发送到后端服务,最终得到完整的文件并成功返回。

但是,有时请求会失败,我的回调被这个err调用:

TypeError: The header content contains invalid characters 
    at ClientRequest.OutgoingMessage.setHeader (_http_outgoing.js:360:11) 
    at new ClientRequest (_http_client.js:85:14) 
    at Object.exports.request (http.js:31:10) 
    at Object.exports.request (https.js:199:15) 
    at Request.start (/app/node_modules/request/request.js:744:32) 
    at Request.write (/app/node_modules/request/request.js:1421:10) 
    at PassThrough.ondata (_stream_readable.js:555:20) 
    at emitOne (events.js:96:13) 
    at PassThrough.emit (events.js:188:7) 
    at PassThrough.Readable.read (_stream_readable.js:381:10) 
    at flow (_stream_readable.js:761:34) 
    at resume_ (_stream_readable.js:743:3) 
    at _combinedTickCallback (internal/process/next_tick.js:80:11) 
    at process._tickDomainCallback (internal/process/next_tick.js:128:9) 

我无法解释该错误的来源,因为我只设置了 Content-Type 标头并且堆栈不包含我的任何代码。

为什么我的上传偶尔会失败?

【问题讨论】:

    标签: node.js express multipartform-data requestjs multiparty


    【解决方案1】:

    此示例说明如何将文件作为附件发送,文件名中包含国家符号。

    const http = require('http');
    const fs = require('fs');
    const contentDisposition = require('content-disposition');
    ...
    
    // req, res - http request and response
    let filename='totally legit ?.pdf';
    let filepath = 'D:/temp/' + filename;               
    
    res.writeHead(200, {
        'Content-Disposition': contentDisposition(filename), // Mask non-ANSI chars
        'Content-Transfer-Encoding': 'binary',
        'Content-Type': 'application/octet-stream'
    });
    
    var readStream = fs.createReadStream(filepath);
    readStream.pipe(res);
    readStream.on('error', (err) => ...);
    

    【讨论】:

    • 链接到content-dispositionlib
    • 替代方式:使用encodeURI。 “内容处置”:encodeURI(文件名),
    【解决方案2】:

    那个TypeErrorgets thrown by node在发出传出HTTP请求时如果请求中有任何字符串headers选项对象包含一个字符outside the basic ASCII range

    在这种情况下,似乎Content-Disposition 标头已在请求中设置,即使它从未在请求​​选项中指定。由于该标头包含上传的文件名,如果文件名包含非 ASCII 字符,则可能导致请求失败。即:

    POST /upload HTTP/1.1
    Host: public-server
    Content-Type: multipart/form-data; boundary=--ex
    Content-Length: [bytes]
    
    ----ex
    Content-Disposition: form-data; name="file"; filename="totally legit ?.pdf"
    Content-Type: application/pdf
    
    [body bytes...]
    ----ex--
    

    other-service/process-file 的请求随后会失败,因为多方将部件标头存储在part 对象上,该对象也是表示部件主体的可读流。当你把pipe()part变成serviceRequest,请求模块looks to see if the piped stream has a headers property, and if it does, copies them to the outgoing request headers

    这会产生如下所示的传出请求:

    POST /process-file HTTP/1.1
    Host: other-service
    Content-Type: application/octet-stream
    Content-Disposition: form-data; name="file"; filename="totally legit ?.pdf"
    Content-Length: [bytes]
    
    [body bytes...]
    

    ...除了该节点在 Content-Disposition 标头中看到非 ASCII 字符并抛出。抛出的错误被请求捕获并作为err传递给请求回调函数。

    可以通过在将部分标头传递到请求之前删除部分标头来避免这种行为。

    delete part.headers;
    part.pipe(serviceRequest);
    

    【讨论】:

      【解决方案3】:

      就像之前的 @arrow cmt 一样,在 Content-disposition 标头上使用 encodeURI(filename)。在客户端,您使用 decodeURI 方法进行解码。

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2016-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-28
      • 2019-01-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多