摘要
这次实现补了好几个以前遗漏的点
1. form 表单 type=file 的 input 提交的file是固定类型的对象
包括 file 多个属性
2. 手动如何拼接 form data
拼接的时候尝试用 file reader 获取到的二进制手动创造 File 对象,失败... 对比发现和前台获取的file 对象缺失很多属性,比如name、lastModified 、type 等等。尝试用 blob 对象(file 的基类)与后端交互也失败,可能某些能解析二进制文件内容的后端框架可以实现直接传文件内容就实现交互,我们现在用的是webx 比较古老的框架。
拼接的form data 如下
multipart/form-data主要由三部分组成:
HTTP Header。需要添加头"Content-Type: multipart/form-data; boundary=%s",这个boundary就是分隔符,见第二条。
分隔符boundary。分隔符是一串和正文内容不冲突的字符串,用以分割多个参数。一般都是N个减号+随机字符串,比如"----------当前时间"。
正文需要加header:
Content-Disposition: form-data; name="%s",%s为需要传递的变量名。
Content-Type: 指定正文MIME类型,默认是纯文本text/plain,未知类型可以填application/octet-stream。
数据。要注意的是数据的编码,文档上说"7BIT encoding",ISO-8859-1即可
备注: File 对象解释 https://developer.mozilla.org/en-US/docs/Web/API/File
3. 提交的请求 content type
不要手动设置, 我当初手动设置 multi-part/data , 但是少了浏览器自动生成的boundary参数,缺失也无法解析。
下面是几个常见的Content-Type:
1.text/html
2.text/plain
3.text/css
4.text/javascript
5.application/x-www-form-urlencoded
6.multipart/form-data
7.application/json
8.application/xml
具体项目结构及代码实现
前台
<input type="file" onChange={this.uploadSuccess} accept=".xlsx" id="uploadFile"/>
<input type="file" onChange={this.uploadSuccess} accept=".xlsx" id="uploadFile"/>
uploadSuccess = (e) => {
const { changeUploadFile } = this.props;
const _this = this;
var reader = new FileReader();
reader.onload = function (readerEvent) {
// 存储上传的文件
changeUploadFile(readerEvent.target.result); // !! 这里可以获取到文件的二进制内容
_this.props.searchBatchUploadEntity(document.getElementById('uploadFile').files[0]);
};
reader.readAsText(e.target.files[0]);
}
shared worker 侧
function wrapFormData(options) {
const formData = new FormData();
for (var key of Object.keys(options)) {
let value = options[key];
formData.append(key, value);
}
return formData;
}
function fetchApi(url,init){
init = init || {};
init.headers = init.headers || {};
init.credentials = 'include'; //for pass cookie
if(init.body){
init.method = init.method || 'POST';
}
init.headers['X-Proxy-Timeout'] = init.timeout || defalutTimeout;
var bodyJson = false;
if(Object.prototype.toString.call(init.body) === '[object Object]'){
bodyJson = true;
init.body = JSON.stringify(init.body);
}
console.time('[performance]_network',url)
return fetch(url,init).then((res) => {
console.timeEnd('[performance]_network',url)
if(res.status === 304) {
return 304;
}
if(bodyJson){
init.body = JSON.parse(init.body);
}
if(!res.ok){
throw new Error(`${res.status} ${res.statusText}`);
}
console.time('[performance]_parse',url)
let resu = res.json();
console.timeEnd('[performance]_parse',url)
return resu;
},(err) => {
throw err;
});
}
function fetchSuccess (url,init) {
console.time('[performance]_'+url)
return fetchApi(url,init).then((data) => {
console.timeEnd('[performance]_'+url)
if(data === 304) {
return 304;
}
if(data.returnCode){
throw new Error(data.returnMessage || JSON.stringify(data));
}
return data.returnValue;
},(err) => {
throw err;
});
}
const init = {
method: 'post',
body: wrapFormData({
graphName,
file,
}),
Accept: 'application/json, application/xml, text/plain, text/html, *.*',
}
fetchSuccess(ROUTER.SEARCH_BATCH_UPLOAD_ENTITY, init).then((result) => {
cb(null, result);
}, (err) => {
cb(err);
});