"标题内容包含无效字符"将多部件上传部件管理到新请求时出错

时间:2017-09-07 21:31:25

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

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

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);
});

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

然而, 有时 请求失败,我的回调会被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标头,而且堆栈中不包含任何代码。

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

2 个答案:

答案 0 :(得分:3)

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

在这种情况下,即使在请求选项中从未指定Content-Disposition标头,也会在请求中设置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-- 标头。由于该标头包含上载的文件名,如果文件名包含非ASCII字符,则可能导致请求失败。即:

other-service/process-file

然后part的请求失败,因为多方将部件标题存储在pipe()对象上,该对象也是表示部件正文的可读流。当您part serviceRequest进入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...] 时,请求模块looks to see if the piped stream has a headers property, and if it does, copies them to the outgoing request headers

这导致传出请求看起来像:

Content-Disposition

...除了节点在err标题中看到非ASCII字符并抛出。抛出的错误由请求捕获并作为delete part.headers; part.pipe(serviceRequest); 传递给请求回调函数。

通过在将部分标题汇总到请求之前删除部分标题,可以避免此行为。

What's my user agent?

答案 1 :(得分:0)

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

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) => ...);