fetch 实现文件上传

Posted 爱抓鱼的大脸猫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);
});

以上是关于fetch 实现文件上传的主要内容,如果未能解决你的问题,请参考以下文章

java Ftp上传创建多层文件的代码片段

大文件上传下载实现思路,分片断点续传代码实现,以及webUpload组件

java前后分离使用fetch上传文件失败500

fetch上传图片(formData数据格式)失败之解决办法

React Native Fetch / Axios文件上传错误

使用 fetch API vuejs 上传文件返回 419 未知状态