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数据然后解析,多线程处理未上传的文件分片。

  • 相关阅读:
    Postgresql HStore 插件试用小结
    postgres-xl 安装与部署 【异常处理】ERROR: could not open file (null)/STDIN_***_0 for write, No such file or directory
    GPDB 5.x PSQL Quick Reference
    postgresql 数据库schema 复制
    hive 打印日志
    gp与 pg 查询进程
    jquery table 发送两次请求 解惑
    python 字符串拼接效率打脸帖
    postgresql 日期类型处理实践
    IBM Rational Rose软件下载以及全破解方法
  • 原文地址:https://www.cnblogs.com/life-of-coding/p/13072245.html
Copyright © 2011-2022 走看看