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數據然后解析,多線程處理未上傳的文件分片。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM