fetch 實現文件上傳


摘要

這次實現補了好幾個以前遺漏的點

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);
});


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM