摘要
這次實現補了好幾個以前遺漏的點
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);
});
