• 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);
    });
    
    
  • 相关阅读:
    Java开发常用Util工具类
    冒泡排序
    EMQ 消息服务器
    将jar文件包打成exe文件
    mina框架搭建tcp服务器:编写自定义协议及编解码器
    SpringBoot中定时任务的设置
    SpringBoot项目+Shiro(权限框架)+Redis(缓存)集成
    计算两个时间之间的天数
    关于extern的使用
    ADC采样间隔问题+TRGO作为ADC的触发源头
  • 原文地址:https://www.cnblogs.com/huxiaoyun90/p/8475989.html
Copyright © 2020-2023  润新知