zoukankan      html  css  js  c++  java
  • Vue.Js(html5) + Java实现文件分片上传

    说明

    代码从项目中剥离修改,未经测试,仅提供思路。

    前端

    upload(file) {
      //从后台获取已经上传的文件分片数
      getIdx(md5)
        .then(function(res) {
          let retry = 3;
          uploadPart(retry, file, res.data);
        })
        .catch();  
    }
    
    uploadPart(retry, file, idx) {
      //设置分片大小(单位Byte)
      let bufferLength = 1024 * 1024 * 5;
      //计算开始的切割点,idx是上传成功的分片数,未上传过文件则开始点为0
      let start = idx * bufferLength;
      //全部上传完毕或重试次数用完则退出
      if(start>=file.size || retry<=0) return;
      //计算分割的位置
      let end = start + bufferLength;
      //如果分割点超出文件大小,回退分割点
      if (end > file.size) {end = fileSize;}
      //切割文件
      var chunk = file.slice(start, end);
      //创建 formData 对象并添加数据
      let formData = new FormData();
      formData.set("file", chunk);
      //如果是第一次上传,连同文件块数量也上传
      if (start == 0) {  
        //计算文件切片总数,向上取整
        let chunkNum = Math.ceil(file.size / bufferLength);
        formData.set("total", chunkNum);
      }
      //上传文件的api,此处使用axios发送请求
      doUpload(formData)
        //发送成功,则上传下一片,递归调用方法
        .then(function() {
          retry = xx;//刷新重试次数
          uploadPart(retry, file, ++idx);
        })
        //发送失败
        .catch(function() {
          retry--;//重试次数减一
          //重试上传这一片
          uploadPart(retry, file, idx);
        });
    },
    

    文件分片上传的前端关键代码只有一句:

    //切割文件
    var chunk = file.slice(start, end);
    

    通过slice方法来切割文件,然后文件上传的流程视业务和具体技术而定,此处是使用axios发送请求,用递归调用上传文件块。
    需要注意的是,Blob.slice(start, end),文件块包含start指向的字节,而不包含end指向的字节,在使用时要注意Blob的边界。

    mozilla对slice的说明

    后端

    /**合并文件的实际操作*/
    public static void doMergeFiles(String outFile, String[] files) {
        //设置缓存大小
        int BUFSIZE = 1024 * 1024;
        //排序。文件后缀名是文件的顺序。
        Arrays.sort(files);
        //输出流
        FileChannel outChannel = null;
        //标记最后的一个文件
        String lastFlag = files[files.length-1];
        try {
            outChannel = new FileOutputStream(outFile).getChannel();
            //遍历文件列表
            for(String f : files){
                //最后一块文件用真实大小设置缓存,避免自动填充数据造成的md5不一致
                if(lastFlag.equals(f)){
                    File last = new File(f);
                    BUFSIZE = (int) last.length();//获取文件的大小并设置成缓存的大小
                }
                FileChannel fc = new FileInputStream(f).getChannel();
                //用ByteBuffer创建缓存
                ByteBuffer bb = ByteBuffer.allocate(BUFSIZE);
                while(fc.read(bb) != -1){//把数据读到缓存
                    bb.flip();//重置游标
                    outChannel.write(bb);//写入数据
                    bb.clear();//清空数据
                }
                fc.close();//关闭流
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            try {if (outChannel != null) {outChannel.close();}} catch (IOException ignore) {}
        }
    }
    

      后端的关键是合并文件,当上传完最后一块文件就进行文件的合并。使用ByteBuffer缓存,使用FileChannel进行文件的读写完成合并操作。在保存文件时,文件名取一致,文件的后缀名则取文件块的顺序,比如第一块文件是“xxx.01”,第10块是“xxx.10”,注意,个位数前面要补“0”,这样可以直接用Array.sort()进行排序。
      为提高性能,可以适当设置缓存大小,可以边上传文件边合并,不必等到文件都上传了才合并。

    拓展

      此处的文件上传是一次上传一片,上传成功才开始上传下一片。如果前端不是使用javascript,能开启使用多线程的话,可以改成同时上传多片文件提高上传速度。已经上传的文件分片用bitmap存储,上传文件前,从后台获取已上传的文件分片的bitmap数据然后解析,多线程处理未上传的文件分片。

  • 相关阅读:
    linux下自动同步internet时间
    String,StringBuffer与StringBuilder的区别
    Spring Boot CLI安装
    java中Date与String的相互转化
    MyBatis Sql语句中的转义字符
    MyBatis详解 与配置MyBatis+Spring+MySql
    MyBatis的foreach语句详解
    不可变集合 Immutable Collections
    Java日期时间使用总结
    Java将一段逗号分割的字符串转换成一个数组
  • 原文地址:https://www.cnblogs.com/life-of-coding/p/13072245.html
Copyright © 2011-2022 走看看