我意识到这个问题已经有 7 个月的历史了,但我将在这里回答它,以帮助目前正在努力解决这个问题的其他人。
你有两个选择,真的:添加文件大小,或者使用 Request 以外的东西。
注意:我在第一次发布后不久对其进行了编辑,希望能提供更多的背景信息。
使用其他东西
如果您不需要它具有的所有内置功能,则可以使用一些替代方法来代替 Request。
form-data
可以在简单的情况下单独使用,也可以与got
. request
在内部使用它。
bhttp
宣传 Streams2+ 支持,但根据我的经验,Streams2+ 支持对我来说不是问题。没有内置https
支持,您必须指定自定义代理
got
另一个瘦了一个。没有像 do 那样对表单数据进行任何特殊处理request
,但通常与form-data
or一起使用form-data2
。不过,我很难让它通过公司代理运行,但这很可能是因为我是网络新手。
needle
看起来很轻,但我还没有真正尝试过。
使用请求:添加文件大小
请求(截至撰写时)不支持使用transfer-encoding: chunked
so 上传文件,您需要将文件的大小与文件一起添加,如果您从 Web 客户端上传,则意味着客户端需要发送该文件除了文件本身之外,您的服务器的大小。
我想出的方法是在文件字段之前在自己的字段中发送文件元数据。
我用描述我所做的评论修改了你的例子。请注意,我没有对收到的数据进行任何验证,但我建议您添加它。
var express = require("express");
var request = require("request");
var BusBoy = require("busboy");
var router = express.Router();
router.post("/upload", function(req, res, next) {
var busboy = new BusBoy({ headers: req.headers });
var json = {};
// Use this to cache any fields which are file metadata.
var fileMetas = {};
busboy.on("file", function (fieldname, file, filename, encoding, mimetype) {
// Be sure to match this prop name here with the pattern you use to detect meta fields.
var meta = fileMetas[fieldname + '.meta'];
if (!meta) {
// Make sure to dump the file.
file.resume();
// Then, do some sort of error handling here, because you cannot upload a file
// without knowing it's length.
return;
}
file.on("data", function(data) {
console.log(`streamed ${data.length}`);
});
file.on("end", function() {
console.log(`finished streaming ${filename}`);
});
var r = request({
url: "http://<my_cloud_storage_api_url>",
method: "POST",
headers: {
"CUSTOM-HEADER": "Hello",
},
formData: {
// value + options form of a formData field.
"upload": {
value: file,
options: {
filename: meta.name,
knownLength: meta.size
}
}
}
}, function(err, httpResponse, body) {
console.log("uploaded");
json.response = body;
});
});
busboy.on("field", function(name, val) {
// Use whatever pattern you want. I used (fileFieldName + ".meta").
// Another good one might be ("meta:" + fileFieldName).
if (/\.meta$/.test(name)) {
// I send an object with { name, size, type, lastModified },
// which are just the public props pulled off a File object.
// Note: Should probably add error handling if val is somehow not parsable.
fileMetas[name] = JSON.parse(val);
console.log(`file metadata: name: ${name}, value: ${value}`);
return;
}
// Otherwise, process field as normal.
console.log(`name: ${name}, value: ${value}`);
});
busboy.on("finish", function() {
res.send(json);
});
req.pipe(busboy);
});
module.exports = router;
在客户端上,您需要在文件本身之前在所谓的字段上发送元数据。这可以通过<input type="hidden">
在文件之前订购一个控件并更新其值来完成onchange
。 发送的值的顺序保证遵循外观中的输入顺序。如果您自己使用 构建请求正文FormData
,则可以通过在附加File
.
示例<form>
<script>
function extractFileMeta(file) {
return JSON.stringify({
size: file.size,
name: file.name,
type: file.type,
lastUpdated: file.lastUpdated
});
}
function onFileUploadChange(event) {
// change this to use arrays if using the multiple attribute on the file input.
var file = event.target.files[0];
var fileMetaInput = document.querySelector('input[name=fileUpload.meta]');
if (fileMetaInput) {
fileMetaInput.value = extractFileMeta(file);
}
}
</script>
<form action="/upload-to-cloud">
<input type="hidden" name="fileUpload.meta">
<input type="file" name="fileUpload" onchange="onFileUploadChange(event)">
</form>
示例FormData
:
function onSubmit(event) {
event.preventDefault();
var form = document.getElementById('my-upload-form');
var formData = new FormData();
var fileUpload = form.elements['fileUpload'];
var fileUploadMeta = JSON.stringify({
size: fileUpload.size,
name: fileUpload.name,
type: fileUpload.type,
lastUpdated: fileUpload.lastUpdated
});
// Append fileUploadMeta BEFORE fileUpload.
formData.append('fileUpload.meta', fileUploadMeta);
formData.append('fileUpload', fileUpload);
// Do whatever you do to POST here.
}