我相信,如果你想做 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);
}
}