[TOC]

链接:样例 源码在本地(文件上传与下载)

文件上传

前端

  • form需要设置enctype参数为multipart/form-data,表示编码方式为文件类型兼表单数据类型(即以二进制流传输表单的value值和文件),这关系到后端HttpServletRequest能否转为MultipartHttpServletRequest
    • 默认的一般是application/x-www=form-urlencoded ,该属性只传输表单的value,不传输文件
    • 还有一个类型text/plain 将空格转换为+号,其它字符不做编码处理,适用于通过表单发送邮件。
    • 有时候即使设置了form-data,上传时也不是文件请求,所以最好使用现成的前端组件,如layui
  • input如果需要多文件上传功能,就添加multiple="multiple" 上传时使用ctrl可以一次选中多个文件上传,但是不能分多次上传,分多次后面会把前面的缓存数据冲掉
    • 文件表单需要设置**name**属性,后端会根据name来取文件表单数据
  • 使用new FormData($('#uploadForm')[0]);获取一个文件类型对象
  • 文件上传控件原生html中只有type="file"可以实现文件上传
    • 对文件上传**input**设置值是无效的,但是可以在提交时获取文件路径。一般用于对文件进行限制,如获取后缀限制类型
    • intput file时设置accept=".xls,.xlsx"可以指定上传文件的类型,不过用户选择时还是能下拉选择全部类型选项
  • form表单实现文件上传有如下要求:
    • 最好采用**post**方式提交,因为**post**携带数据量更大 ```html
      文件:

      <a name="Ez56Z"></a>
      ## layui文件上传
      
      - **为post类型**
      ```javascript
      layui.use('upload', function(){
        var upload = layui.upload;
        var uploadInst =  upload.render({
          elem: '#file',  //绑定的input
          accept:'file',  //表示文件的大致类型,如文件,媒体,默认为图片
          data: {  //额外携带的参数
            "mid":mid2,
            "cid":cid
          },
          auto: false,  //不自动提交
          exts:'xls|xlsx',  //具体类型
          bindAction:'#daorubutton',   //手动提交的按钮
          headers: {sign: config.headers('daoru').sign}  //数字验签
          ,url: config.wage()+'/exceltodbModel/daoru' //此处配置你自己的上传接口即可
          //此处配置你自己的上传接口即可
          ,done: function(res){
            layer.msg('上传成功');
            console.log(res)
          }
        });
       });
      

      后端

      文件上传配置

      • 这里以springboot为准,注意springboot有默认文件最大大小限制(不清楚是1MB还是10MB)。通过如下方式可以修改,前端也可以通过表单控件的属性进行限制
        • 单位大小,KB,MB,GB可以设置,其他如字节,TB等单位没试过
      • location属性是设置文件本地缓存位置的配置,写配置文件里有个好处是在编译器就会检查该路径是否存在,不存在就编译报错。避免了运行时路径的io异常和空指针
        • file-size-threshold: 5MB #指定文件将写入磁盘的大小阈值。 默认值为0。意味文件大小必须大于该值时才允许写入磁盘 ```properties

      单个文件最大大小

      spring.servlet.multipart.max-file-size = 10MB

      总上传的数据最大大小

      spring.servlet.multipart.max-request-size = 1GB

      spring: servlet: multipart: enabled: true location: C:/Users/yxkj/Documents/code
      file-size-threshold: 5MB #指定文件将写入磁盘的大小阈值。 默认值为0 max-file-size: 20MB

      <a name="AaJJo"></a>
      ### 文件上传接收
      
      - 这里写了个工具类,可以对上传的文件进行选择,不符合要求的过滤,主要依靠这些方法:
         - `MultipartHttpServletRequest=(MultipartHttpServletRequest) HttpServletRequest`可以得到一个文件请求体对象,
            - 注意普通表单数据必须通过控制器映射或者request获得,MultipartHttpServletRequest无法获得表单数据
         - 单个文件可以直接`@RequestBody MultipartFile`来接收,文件与参数一起传用`@RequestParam(...) Mul...`
      - `MultipartHttpServletRequest`对象可以调用`getFiles(name)`得到`MultipartFile`对象或者对象集合
         - name是`input`的name属性参数
      - `MultipartFile`存储一个文件及其各种信息:
         - `getSize()`:文件大小
         - `getContentType()`  得到文件类型,得到的类型是`contentType`,对应关系见[contentType对应表](https://www.runoob.com/http/http-content-type.html)
         - `getOriginalFilename`得到原始文件名
            - 还有个`getName()`方法,它得到的永远都是`"file"`字符串,表示这是文件类型
         - `getInputStream()`得到Multipart的输入流
      
      ```java
             MultipartHttpServletRequest multipartRequest = null;
              try {
                  //需要设置enctype为multipare才能进行强转
                  multipartRequest = (MultipartHttpServletRequest) httpServletRequest;
              } catch (Exception e) {
                  return new LinkedList<MultipartFile>();
              }
      
              List<MultipartFile> files = new LinkedList<>();
              files = multipartRequest.getFiles("file");
      
       public HttpResult daoru(@RequestParam("file") MultipartFile file,@RequestParam("mid")String mid
      

      springboot API优化接口

      • transferTo原本在io中是将输入流传到输出流中,springmvc提供了一个类似的功能,让MultipartFile对象调用自己的transferTo并传个路径对象,即可快速输出到磁盘
      • subBefore``subAfter快速根据分割字符拆分 获取前缀和后缀

      • 文件下载

        后端

      • 必须设置的响应信息如下:

        • ContentType:文件类型,也可以全设置为application/force-download
        • Header:文件名,还可以设置其他额外信息
        • setCharacterEncoding(charset) 文件名如果有中文,设置utf-8才能正确显示
        • 其他的就不是必须的设置项
      • getOutputStream()HttpServletResponse中得到输出流,往输出流中写入信息即可
      • 使用a标签或者href可以自动跳转请求get下载接口,因为href默认为get请求,而ajax交互是以字符串进行交流,不会自动调用浏览器下载响应内容
        • 但是这种方式对于认证与验签的请求不行,这时必须使用ajax或者原生xhr。以添加token等认证信息
      • header允许存在同名header信息

        • addheader:无则添加,有也添加
        • setheader:无则添加,有则覆盖
          @GetMapping("/download")
          public String downloadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
             String files = "C:\\Users\\Lw\\Desktop\\xdf.txt";
             if (files != null) {
                 File file = new File(files);
                 if (file.exists()) {
                     OutputStream os = response.getOutputStream();
                     FileInputStream fis = new FileInputStream(files);
                     byte[] buffer = new byte[fis.available()];
                     try {
                         fis.read(buffer);   //不写这一步,buffer只是存储了?长度的0,写进os里也算空白数据。走了这一步才会变成?长度的实际数据,
                         response.setContentType("text/plain");// 设置强制下载不打开   text/plain
                         response.addHeader("Content-Disposition", "attachment;fileName=" + "test.txt");// 设置文件名
                         response.setHeader("Content-Range", "" + Integer.valueOf(buffer.length - 1));
                         os.write(buffer);
                         return "下载成功";
                     } catch (Exception e) {
                         e.printStackTrace();
                         return "下载失败";
                     } finally { // 做关闭操作
                         if (buffer != null) {
                             try {
                                 os.flush();
                                 os.close();
                                 fis.close();
                             } catch (IOException e) {
                                 e.printStackTrace();
                             }
                         }
                     }
                 } else {
                     return "文件不存在";
                 }
             } else {
                 return "路径错误";
             }
          }
          

          常用响应头

      • application/x-xls xls文件

        ajax实现文件下载

      • ajax实现文件下载,一般是为了在请求文件下载的同时进行鉴权,设置请求头等

      • 通过ajax下载文件,响应里面能看到文件数据(一般是乱码)

        function download(ajaxType,url) {  //ajax请求类型:'post'/'type'
          const xhr = new XMLHttpRequest();
          xhr.open(ajaxType, url, true); //默认async为true
          xhr.setRequestHeader('token', sessionStorage.getItem('token')); // 设置token
          xhr.setRequestHeader('Content-Type', 'application/octet-stream');
          xhr.responseType = 'blob'; // 返回类型blob
          xhr.onload = function(e) {
              if (this.status === 200) {
                  const blob = this.response;
                  const reader = new FileReader();
                  reader.readAsDataURL(blob); // 转换为base64,可以直接放入a表情href
                  reader.onload = function(e) {
                      const a = document.createElement('a');
                      a.download = '文件名.xls';
                      a.href = e.target.result;
                      document.documentElement.appendChild(a);
                      a.click();
                      a.remove(); // 等价于document.documentElement.removeChild(a);
                  };
              }
          };
          xhr.send(); // 发送ajax请求
        }
        

        axios实现文件下载

        ```javascript download2() {

          const url = '导出链接';
        

        $get(url , {

          startDate: '2021-08-01',
          endDate: '2021-08-01'
        

        }).then(res => {

          res.request.onload = function(e) {
              const blob = res.data;
              const reader = new FileReader();
              reader.readAsDataURL(blob);
              reader.onload = function(e) {
                  const a = document.createElement('a');
                  a.download = '文件名.xls';
                  a.href = e.target.result;
                  document.documentElement.appendChild(a);
                  a.click();
                  a.remove();
              };
          };
        

        }); } // axios代码 const req = axios.create({ responseType: ‘blob’ //关键 }); req.interceptors.request.use( config => {

          config.headers = {
              token: sessionStorage.getItem('token'),
              'Content-Type': 'application/octet-stream'
          };
          return config;
        

        }, err => {

          return Promise.reject(err);
        

        } ); req.interceptors.response.use( res => {

          // 这里做错误判断(这里假设有token直接返回文件流 没有token返回的res包含code)
          if (res.hasOwnProperty('code') && res.code !== 0) {
              alert(res.message || '导出错误');
              return;
          }
        
          return res;
        

        }, err => {

          console.log(err, err.message);
        

        } );

      function $get(url, params = {}) { return new Promise((resolve, reject) => { req.get(url, { params }).then( res => { resolve(res); }, err => { reject(err); } ); }); }

      ```