我的快递服务器接收来自浏览器的文件上传。上传内容将作为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
标头,而且堆栈中不包含任何代码。
为什么我的上传偶尔会失败?
答案 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) => ...);