将分段上传部分传输到新请求时出现“标题内容包含无效字符”错误

jos*_*736 2 multipartform-data node.js express requestjs multiparty

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

Multiparty 允许您将一部分(粗略地说,单个表单字段,如<input type="file">)作为可读流。我不想在我的 Web 服务器上处理或存储上传的文件,所以我只是将上传的文件部分通过管道传输到另一个服务的请求中(使用请求模块)。

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

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

但是,有时请求会失败并且我的回调会被调用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) 
Run Code Online (Sandbox Code Playgroud)

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

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

Aik*_*wai 9

此示例显示如何将文件作为文件名中带有国家符号的附件发送。

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

  • 替代方法:使用encodeURI。'内容处置':encodeURI(文件名), (3认同)
  • 链接到 [content-disposition](https://github.com/jshttp/content-disposition) lib (2认同)

jos*_*736 5

TypeError 得到由节点抛出进行去电HTTP请求时,如果在该请求的任何字符串headers选项对象包含一个字符的基本ASCII范围之外

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

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

然后请求other-service/process-file失败,因为多方将零件标头存储在part对象上,该对象也是代表零件主体的可读流。当你pipe()part进入serviceRequest时,请求模块,看长相,如果管道流具有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...]
Run Code Online (Sandbox Code Playgroud)

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

可以通过在将部分标题通过管道传输到请求之前删除它来避免这种行为。

delete part.headers;
part.pipe(serviceRequest);
Run Code Online (Sandbox Code Playgroud)