<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Java大文件上傳、分片上傳、多文件上傳、斷點續傳、上傳文件minio、分片上傳minio等解決方案

      • 上傳說明

               文件上傳花樣百出,根據不同場景使用不同方案進行實現尤為必要。通常開發過程中,文件較小,直接將文件轉化為字節流上傳到服務器,但是文件較大時,用普通的方法上傳,顯然效果不是很好,當文件上傳一半中斷再次上傳時,發現需要重新開始,這種體驗不是很爽,下面介紹幾種好一點兒的上傳方式。

      這里講講如何在Spring boot 編寫上傳代碼,如有問題可以在下留言,我并在文章末尾附上Java上傳源碼供大家下載。

        • 分片上傳

            分片上傳,就是將所要上傳的文件,按照一定的大小,將整個文件分
      隔成多個數據塊(我們稱之為Part)來進行分別上傳,上傳完之后再
      由服務端對所有上傳的文件進行匯總整合成原始的文件。

        • 斷點續傳

                斷點續傳是在下載/上傳時,將下載/上傳任務(一個文件或一個壓縮
      包)人為的劃分為幾個部分,每一個部分采用一個線程進行上傳/下載,
      如果碰到網絡故障,可以從已經上傳/下載的部分開始繼續上傳/下載
      未完成的部分,而沒有必要從頭開始上傳/下載。

      • Redis啟動安裝

      Redis安裝包分為 Windows 版和 Linux 版:
      Windows版下載地址:https://github.com/microsoftarchive/redis/releases
      Linux版下載地址: https://download.redis.io/releases/
      我當前使用的Windows版本:

       

       

      • minio下載啟動

      windows版本可以參考我之前的文檔:window10安裝minio_minio windows安裝-CSDN博客

      啟動會提示:

       

      以上是密碼設置問題需要修改如下:

      set MINIO_ROOT_USER=admin
      set MINIO_ROOT_PASSWORD=12345678

      啟動成功后會輸出相應地址

      • 上傳后端Java代碼

         后端采用Spring boot項目結構,主要代碼如下:

        1   /**
        2      * 單文件上傳
        3      * 直接將傳入的文件通過io流形式直接寫入(服務器)指定路徑下
        4      *
        5      * @param file 上傳的文件
        6      * @return
        7      */
        8     @Override
        9     public ResultEntity<Boolean> singleFileUpload(MultipartFile file) {
       10         //實際情況下,這些路徑都應該是服務器上面存儲文件的路徑
       11         String filePath = System.getProperty("user.dir") + "\\file\\";
       12         File dir = new File(filePath);
       13         if (!dir.exists()) dir.mkdir();
       14  
       15         if (file == null) {
       16             return ResultEntity.error(false, "上傳文件為空!");
       17         }
       18         InputStream fileInputStream = null;
       19         FileOutputStream fileOutputStream = null;
       20         try {
       21             String filename = file.getOriginalFilename();
       22             fileOutputStream = new FileOutputStream(filePath + filename);
       23             fileInputStream = file.getInputStream();
       24  
       25             byte[] buf = new byte[1024 * 8];
       26             int length;
       27             while ((length = fileInputStream.read(buf)) != -1) {//讀取fis文件輸入字節流里面的數據
       28                 fileOutputStream.write(buf, 0, length);//通過fos文件輸出字節流寫出去
       29             }
       30             log.info("單文件上傳完成!文件路徑:{},文件名:{},文件大小:{}", filePath, filename, file.getSize());
       31             return ResultEntity.success(true, "單文件上傳完成!");
       32         } catch (IOException e) {
       33             return ResultEntity.error(true, "單文件上傳失敗!");
       34         } finally {
       35             try {
       36                 if (fileOutputStream != null) {
       37                     fileOutputStream.close();
       38                     fileOutputStream.flush();
       39                 }
       40                 if (fileInputStream != null) {
       41                     fileInputStream.close();
       42                 }
       43             } catch (Exception e) {
       44                 e.printStackTrace();
       45             }
       46         }
       47     }
       48  
       49     /**
       50      * 多文件上傳
       51      * 直接將傳入的多個文件通過io流形式直接寫入(服務器)指定路徑下
       52      * 寫入指定路徑下是通過多線程進行文件寫入的,文件寫入線程執行功能就和上面單文件寫入是一樣的
       53      *
       54      * @param files 上傳的所有文件
       55      * @return
       56      */
       57     @Override
       58     public ResultEntity<Boolean> multipleFileUpload(MultipartFile[] files) {
       59         //實際情況下,這些路徑都應該是服務器上面存儲文件的路徑
       60         String filePath = System.getProperty("user.dir") + "\\file\\";
       61         File dir = new File(filePath);
       62         if (!dir.exists()) dir.mkdir();
       63  
       64         if (files.length == 0) {
       65             return ResultEntity.error(false, "上傳文件為空!");
       66         }
       67         ArrayList<String> uploadFiles = new ArrayList<>();
       68         try {
       69  
       70             ArrayList<Future<String>> futures = new ArrayList<>();
       71             //使用多線程來完成對每個文件的寫入
       72             for (MultipartFile file : files) {
       73                 futures.add(partMergeTask.submit(new MultipleFileTaskExecutor(filePath, file)));
       74             }
       75  
       76             //這里主要用于監聽各個文件寫入線程是否執行結束
       77             int count = 0;
       78             while (count != futures.size()) {
       79                 for (Future<String> future : futures) {
       80                     if (future.isDone()) {
       81                         uploadFiles.add(future.get());
       82                         count++;
       83                     }
       84                 }
       85                 Thread.sleep(1);
       86             }
       87             log.info("多文件上傳完成!文件路徑:{},文件信息:{}", filePath, uploadFiles);
       88             return ResultEntity.success(true, "多文件上傳完成!");
       89         } catch (Exception e) {
       90             log.error("多文件分片上傳失敗!", e);
       91             return ResultEntity.error(true, "多文件上傳失敗!");
       92         }
       93  
       94     }
       95  
       96     /**
       97      * 單文件分片上傳
       98      * 直接將傳入的文件分片通過io流形式寫入(服務器)指定臨時路徑下
       99      * 然后判斷是否分片都上傳完成,如果所有分片都上傳完成的話,就把臨時路徑下的分片文件通過流形式讀入合并并從新寫入到(服務器)指定文件路徑下
      100      * 最后刪除臨時文件和臨時文件夾,臨時文件夾是通過文件的uuid進行命名的
      101      *
      102      * @param filePart  分片文件
      103      * @param partIndex 當前分片值
      104      * @param partNum   所有分片數
      105      * @param fileName  當前文件名稱
      106      * @param fileUid   當前文件uuid
      107      * @return
      108      */
      109     @Override
      110     public ResultEntity<Boolean> singleFilePartUpload(MultipartFile filePart, Integer partIndex, Integer partNum, String fileName, String fileUid) {
      111         //實際情況下,這些路徑都應該是服務器上面存儲文件的路徑
      112         String filePath = System.getProperty("user.dir") + "\\file\\";//文件存放路徑
      113         String tempPath = filePath + "temp\\" + fileUid;//臨時文件存放路徑
      114         File dir = new File(tempPath);
      115         if (!dir.exists()) dir.mkdirs();
      116  
      117         //生成一個臨時文件名
      118         String tempFileNamePath = tempPath + "\\" + fileName + "_" + partIndex + ".part";
      119         try {
      120             //將分片存儲到臨時文件夾中
      121             filePart.transferTo(new File(tempFileNamePath));
      122  
      123             File tempDir = new File(tempPath);
      124             File[] tempFiles = tempDir.listFiles();
      125  
      126             one:
      127             if (partNum.equals(Objects.requireNonNull(tempFiles).length)) {
      128                 //需要校驗一下,表示已有異步程序正在合并了;如果是分布式這個校驗可以加入redis的分布式鎖來完成
      129                 if (isMergePart.get(fileUid) != null) {
      130                     break one;
      131                 }
      132                 isMergePart.put(fileUid, tempFiles.length);
      133                 System.out.println("所有分片上傳完成,預計總分片:" + partNum + "; 實際總分片:" + tempFiles.length);
      134  
      135                 FileOutputStream fileOutputStream = new FileOutputStream(filePath + fileName);
      136                 //這里如果分片很多的情況下,可以采用多線程來執行
      137                 for (int i = 0; i < partNum; i++) {
      138                     //讀取分片數據,進行分片合并
      139                     FileInputStream fileInputStream = new FileInputStream(tempPath + "\\" + fileName + "_" + i + ".part");
      140                     byte[] buf = new byte[1024 * 8];//8MB
      141                     int length;
      142                     while ((length = fileInputStream.read(buf)) != -1) {//讀取fis文件輸入字節流里面的數據
      143                         fileOutputStream.write(buf, 0, length);//通過fos文件輸出字節流寫出去
      144                     }
      145                     fileInputStream.close();
      146                 }
      147                 fileOutputStream.flush();
      148                 fileOutputStream.close();
      149  
      150                 // 刪除臨時文件夾里面的分片文件 如果使用流操作且沒有關閉輸入流,可能導致刪除失敗
      151                 for (int i = 0; i < partNum; i++) {
      152                     boolean delete = new File(tempPath + "\\" + fileName + "_" + i + ".part").delete();
      153                     File file = new File(tempPath + "\\" + fileName + "_" + i + ".part");
      154                 }
      155                 //在刪除對應的臨時文件夾
      156                 if (Objects.requireNonNull(tempDir.listFiles()).length == 0) {
      157                     tempDir.delete();
      158                 }
      159                 isMergePart.remove(fileUid);
      160             }
      161  
      162         } catch (Exception e) {
      163             log.error("單文件分片上傳失敗!", e);
      164             return ResultEntity.error(false, "單文件分片上傳失敗");
      165         }
      166         //通過返回成功的分片值,來驗證分片是否有丟失
      167         return ResultEntity.success(true, partIndex.toString());
      168     }
      169  
      170     /**
      171      * 多文件分片上傳
      172      * 先將所有文件分片讀入到(服務器)指定臨時路徑下,每個文件的分片文件的臨時文件夾都是已文件的uuid進行命名的
      173      * 然后判斷對已經上傳所有分片的文件進行合并,此處是通過多線程對每一個文件的分片文件進行合并的
      174      * 最后對已經合并完成的分片臨時文件和文件夾進行刪除
      175      *
      176      * @param filePart  分片文件
      177      * @param partIndex 當前分片值
      178      * @param partNum   總分片數
      179      * @param fileName  當前文件名稱
      180      * @param fileUid   當前文件uuid
      181      * @return
      182      */
      183     @Override
      184     public ResultEntity<String> multipleFilePartUpload(MultipartFile filePart, Integer partIndex, Integer partNum, String fileName, String fileUid) {
      185         //實際情況下,這些路徑都應該是服務器上面存儲文件的路徑
      186         String filePath = System.getProperty("user.dir") + "\\file\\";//文件存放路徑
      187         String tempPath = filePath + "temp\\" + fileUid;//臨時文件存放路徑
      188         File dir = new File(tempPath);
      189         if (!dir.exists()) dir.mkdirs();
      190         //生成一個臨時文件名
      191         String tempFileNamePath = tempPath + "\\" + fileName + "_" + partIndex + ".part";
      192         try {
      193             filePart.transferTo(new File(tempFileNamePath));
      194  
      195             File tempDir = new File(tempPath);
      196             File[] tempFiles = tempDir.listFiles();
      197             //如果臨時文件夾中分片數量和實際分片數量一致的時候,就需要進行分片合并
      198             one:
      199             if (partNum.equals(tempFiles.length)) {
      200                 //需要校驗一下,表示已有異步程序正在合并了;如果是分布式這個校驗可以加入redis的分布式鎖來完成
      201                 if (isMergePart.get(fileUid) != null) {
      202                     break one;
      203                 }
      204                 isMergePart.put(fileUid, tempFiles.length);
      205                 System.out.println(fileName + ":所有分片上傳完成,預計總分片:" + partNum + "; 實際總分片:" + tempFiles.length);
      206  
      207                 //使用多線程來完成對每個文件的合并
      208                 Future<Integer> submit = partMergeTask.submit(new PartMergeTaskExecutor(filePath, tempPath, fileName, partNum));
      209                 System.out.println("上傳文件名:" + fileName + "; 總大小:" + submit.get());
      210                 isMergePart.remove(fileUid);
      211             }
      212         } catch (Exception e) {
      213             log.error("{}:多文件分片上傳失敗!", fileName, e);
      214             return ResultEntity.error("", "多文件分片上傳失敗");
      215         }
      216         //通過返回成功的分片值,來驗證分片是否有丟失
      217         return ResultEntity.success(partIndex.toString(), fileUid);
      218     }
      219  
      220     /**
      221      * 多文件(分片)秒傳
      222      * 通過對比已有的文件分片md5值和需要上傳文件分片的MD5值,
      223      * 在文件分片合并的時候,對已有的文件進行地址索引,對沒有的文件進行臨時文件寫入
      224      * 最后合并的時候根據不同的文件分片進行文件讀取寫入
      225      *
      226      * @param filePart  上傳沒有的分片文件
      227      * @param fileInfo  當前分片文件相關信息
      228      * @param fileOther 已存在文件分片相關信息
      229      * @return
      230      */
      231     @Override
      232     public ResultEntity<String> multipleFilePartFlashUpload(MultipartFile filePart, String fileInfo, String fileOther) {
      233         DiskFileIndexVo upFileInfo = JSONObject.parseObject(fileInfo, DiskFileIndexVo.class);
      234         List<DiskFileIndexVo> notUpFileInfoList = JSON.parseArray(fileOther, DiskFileIndexVo.class);
      235         //實際情況下,這些路徑都應該是服務器上面存儲文件的路徑
      236         String filePath = System.getProperty("user.dir") + "\\file\\";//文件存放路徑
      237         //正常情況下,這個臨時文件也應該放入(服務器)非臨時文件夾中,這樣方便下次其他文件上傳查找是否曾經上傳過類似的
      238         //當前demo是單獨存放在臨時文件夾中,文件合并完成之后直接刪除的
      239         String tempPath = filePath + "temp\\" + upFileInfo.getFileUid();//臨時文件存放路徑
      240  
      241         File dir = new File(tempPath);
      242         if (!dir.exists()) dir.mkdirs();
      243         //生成一個臨時文件名
      244         String tempFileNamePath = tempPath + "\\" + upFileInfo.getFileName() + "_" + upFileInfo.getPartIndex() + ".part";
      245  
      246         try {
      247             filePart.transferTo(new File(tempFileNamePath));
      248  
      249             File tempDir = new File(tempPath);
      250             File[] tempFiles = tempDir.listFiles();
      251             notUpFileInfoList = notUpFileInfoList.stream().filter(e ->
      252                     upFileInfo.getFileUid().equals(e.getFileUid())).collect(Collectors.toList());
      253             //如果臨時文件夾中分片數量和實際分片數量一致的時候,就需要進行分片合并
      254             one:
      255             if ((upFileInfo.getPartNum() - notUpFileInfoList.size()) == tempFiles.length) {
      256                 //需要校驗一下,表示已有異步程序正在合并了;如果是分布式這個校驗可以加入redis的分布式鎖來完成
      257                 if (isMergePart.get(upFileInfo.getFileUid()) != null) {
      258                     break one;
      259                 }
      260                 isMergePart.put(upFileInfo.getFileUid(), tempFiles.length);
      261                 System.out.println(upFileInfo.getFileName() + ":所有分片上傳完成,預計總分片:" + upFileInfo.getPartNum()
      262                         + "; 實際總分片:" + tempFiles.length + "; 已存在分片數:" + notUpFileInfoList.size());
      263  
      264                 //使用多線程來完成對每個文件的合并
      265                 Future<Integer> submit = partMergeTask.submit(
      266                         new PartMergeFlashTaskExecutor(filePath, upFileInfo, notUpFileInfoList));
      267                 isMergePart.remove(upFileInfo.getFileUid());
      268             }
      269         } catch (Exception e) {
      270             log.error("{}:多文件(分片)秒傳失敗!", upFileInfo.getFileName(), e);
      271             return ResultEntity.error("", "多文件(分片)秒傳失敗!");
      272         }
      273         //通過返回成功的分片值,來驗證分片是否有丟失
      274         return ResultEntity.success(upFileInfo.getPartIndex().toString(), upFileInfo.getFileUid());
      275     }
      276  
      277     /**
      278      * 根據傳入需要上傳的文件片段的md5值來對比服務器中的文件的md5值,將已有對應的md5值的文件過濾出來,
      279      * 通知前端或者自行出來這些文件,即為不需要上傳的文件分片,并將已有的文件分片地址索引返回給前端進行出來
      280      *
      281      * @param upLoadFileListMd5 原本需要上傳文件的索引分片信息
      282      * @return
      283      */
      284     @Override
      285     public ResultEntity<List<DiskFileIndexVo>> checkDiskFile(List<DiskFileIndexVo> upLoadFileListMd5) {
      286         List<DiskFileIndexVo> notUploadFile;
      287         try {
      288             //后端服務器已經存在的分片md5值集合
      289             List<DiskFileIndexVo> diskFileMd5IndexList = diskFileIndexVos;
      290  
      291             notUploadFile = upLoadFileListMd5.stream().filter(uf -> diskFileMd5IndexList.stream().anyMatch(
      292                     df -> {
      293                         if (df.getFileMd5().equals(uf.getFileMd5())) {
      294                             uf.setFileIndex(df.getFileName());//不需要上傳文件的地址索引
      295                             return true;
      296                         }
      297                         return false;
      298                     })).collect(Collectors.toList());
      299             log.info("過濾出不需要上傳的文件分片:{}", notUploadFile);
      300         } catch (Exception e) {
      301             log.error("上傳文件檢測異常!", e);
      302             return ResultEntity.error("上傳文件檢測異常!");
      303         }
      304         return ResultEntity.success(notUploadFile);
      305     }
      306  
      307     /**
      308      * 根據文件uuid(md5生成的)來判斷此文件在服務器中是否未上傳完整,
      309      * 如果沒上傳完整,則返回相關上傳進度等信息
      310      *
      311      * @param pointFileIndexVo
      312      * @return
      313      */
      314     @Override
      315     public ResultEntity<PointFileIndexVo> checkUploadFileIndex(PointFileIndexVo pointFileIndexVo) {
      316         try {
      317             List<String> list = uploadProgress.get(pointFileIndexVo.getFileMd5());
      318             if (list == null) list = new ArrayList<>();
      319             pointFileIndexVo.setParts(list);
      320             System.out.println("已上傳部分:" + list);
      321             return ResultEntity.success(pointFileIndexVo);
      322         } catch (Exception e) {
      323             log.error("上傳文件檢測異常!", e);
      324             return ResultEntity.error("上傳文件檢測異常!");
      325         }
      326     }
      327  
      328     /**
      329      * 單文件(分片)斷點上傳
      330      *
      331      * @param filePart 需要上傳的分片文件
      332      * @param fileInfo 當前需要上傳的分片文件信息,如uuid,文件名,文件總分片數量等
      333      * @return
      334      */
      335     @Override
      336     public ResultEntity<String> singleFilePartPointUpload(MultipartFile filePart, String fileInfo) {
      337         PointFileIndexVo pointFileIndexVo = JSONObject.parseObject(fileInfo, PointFileIndexVo.class);
      338         //實際情況下,這些路徑都應該是服務器上面存儲文件的路徑
      339         String filePath = System.getProperty("user.dir") + "\\file\\";//文件存放路徑
      340         String tempPath = filePath + "temp\\" + pointFileIndexVo.getFileMd5();//臨時文件存放路徑
      341         File dir = new File(tempPath);
      342         if (!dir.exists()) dir.mkdirs();
      343  
      344         //生成一個臨時文件名
      345         String tempFileNamePath = tempPath + "\\" + pointFileIndexVo.getFileName() + "_" + pointFileIndexVo.getPartIndex() + ".part";
      346         try {
      347             //將分片存儲到臨時文件夾中
      348             filePart.transferTo(new File(tempFileNamePath));
      349  
      350             List<String> partIndex = uploadProgress.get(pointFileIndexVo.getFileMd5());
      351             if (Objects.isNull(partIndex)) {
      352                 partIndex = new ArrayList<>();
      353             }
      354             partIndex.add(pointFileIndexVo.getPartIndex().toString());
      355             uploadProgress.put(pointFileIndexVo.getFileMd5(), partIndex);
      356  
      357             File tempDir = new File(tempPath);
      358             File[] tempFiles = tempDir.listFiles();
      359  
      360             one:
      361             if (pointFileIndexVo.getPartNum().equals(Objects.requireNonNull(tempFiles).length)) {
      362                 //需要校驗一下,表示已有異步程序正在合并了;如果是分布式這個校驗可以加入redis的分布式鎖來完成
      363                 if (isMergePart.get(pointFileIndexVo.getFileMd5()) != null) {
      364                     break one;
      365                 }
      366                 isMergePart.put(pointFileIndexVo.getFileMd5(), tempFiles.length);
      367                 System.out.println("所有分片上傳完成,預計總分片:" + pointFileIndexVo.getPartNum() + "; 實際總分片:" + tempFiles.length);
      368                 //讀取分片數據,進行分片合并
      369                 FileOutputStream fileOutputStream = new FileOutputStream(filePath + pointFileIndexVo.getFileName());
      370                 //這里如果分片很多的情況下,可以采用多線程來執行
      371                 for (int i = 0; i < pointFileIndexVo.getPartNum(); i++) {
      372                     FileInputStream fileInputStream = new FileInputStream(tempPath + "\\" + pointFileIndexVo.getFileName() + "_" + i + ".part");
      373                     byte[] buf = new byte[1024 * 8];//8MB
      374                     int length;
      375                     while ((length = fileInputStream.read(buf)) != -1) {//讀取fis文件輸入字節流里面的數據
      376                         fileOutputStream.write(buf, 0, length);//通過fos文件輸出字節流寫出去
      377                     }
      378                     fileInputStream.close();
      379                 }
      380                 fileOutputStream.flush();
      381                 fileOutputStream.close();
      382  
      383                 // 刪除臨時文件夾里面的分片文件 如果使用流操作且沒有關閉輸入流,可能導致刪除失敗
      384                 for (int i = 0; i < pointFileIndexVo.getPartNum(); i++) {
      385                     boolean delete = new File(tempPath + "\\" + pointFileIndexVo.getFileName() + "_" + i + ".part").delete();
      386                     File file = new File(tempPath + "\\" + pointFileIndexVo.getFileName() + "_" + i + ".part");
      387                 }
      388                 //在刪除對應的臨時文件夾
      389                 if (Objects.requireNonNull(tempDir.listFiles()).length == 0) {
      390                   tempDir.delete();
      391                 }
      392                 isMergePart.remove(pointFileIndexVo.getFileMd5());
      393                 uploadProgress.remove(pointFileIndexVo.getFileMd5());
      394             }
      395  
      396         } catch (Exception e) {
      397             log.error("單文件分片上傳失敗!", e);
      398             return ResultEntity.error(pointFileIndexVo.getFileMd5(), "單文件分片上傳失敗");
      399         }
      400         //通過返回成功的分片值,來驗證分片是否有丟失
      401         return ResultEntity.success(pointFileIndexVo.getFileMd5(), pointFileIndexVo.getPartIndex().toString());
      402     }
      403  
      404     /**
      405      * 獲取(服務器)指定文件存儲路徑下所有文件MD5值
      406      * 實際情況下,每一個文件的md5值都是單獨保存在數據庫或者其他存儲機制中的,
      407      * 不需要每次都去讀取文件然后獲取md5值,這樣多次io讀取很耗性能
      408      *
      409      * @return
      410      * @throws Exception
      411      */
      412     @Bean
      413     private List<DiskFileIndexVo> getDiskFileMd5Index() throws Exception {
      414         String filePath = System.getProperty("user.dir") + "\\file\\part\\";
      415         File saveFileDir = new File(filePath);
      416         if (!saveFileDir.exists()) saveFileDir.mkdirs();
      417  
      418         List<DiskFileIndexVo> diskFileIndexVoList = new ArrayList<>();
      419         File[] listFiles = saveFileDir.listFiles();
      420         if (listFiles == null) return diskFileIndexVoList;
      421  
      422         for (File listFile : listFiles) {
      423             String fileName = listFile.getName();
      424             FileInputStream fileInputStream = new FileInputStream(filePath + fileName);
      425             String md5DigestAsHex = DigestUtils.md5DigestAsHex(fileInputStream);
      426  
      427             DiskFileIndexVo diskFileIndexVo = new DiskFileIndexVo();
      428             diskFileIndexVo.setFileName(fileName);
      429             diskFileIndexVo.setFileMd5(md5DigestAsHex);
      430             diskFileIndexVoList.add(diskFileIndexVo);
      431             fileInputStream.close();
      432         }
      433  
      434         diskFileIndexVos = diskFileIndexVoList;
      435         log.info("服務器磁盤所有文件 {}", diskFileIndexVoList);
      436         return diskFileIndexVoList;
      437     }

      代碼結構圖:

       

      • 前端代碼

      整體的過程如下
      前端將文件按照百分比進行計算,每次上傳文件的百分之一(文件分片),給文件分片做上序號及文件uuid,然后在循環里面對文件片段上傳的時候在將當前分片值一起傳給后端。
      后端將前端每次上傳的文件,放入到緩存目錄;
      前端將全部的文件內容都上傳完畢后,發送一個合并請求;
      后端合并分片的之后對文件進行命名保存;
      后端保存臨時分片的時候命名索引,方便合并的時候按照分片索引進行合并;

      vue模板代碼:

       1       <!-- 單文件分片上傳 -->
       2       <div class="fileUploadStyle">
       3         <h3>單文件分片上傳</h3>
       4         <el-upload ref="upload" name="files" action="#" :on-change="selectSinglePartFile"
       5           :on-remove="removeSingleFilePart" :file-list="singleFilePart.fileList" :auto-upload="false">
       6           <el-button slot="trigger" size="small" type="primary">選取文件</el-button>
       7           <el-button style="margin-left: 10px;" size="small" type="success"
       8             @click="singleFilePartUpload">點擊進行單文件分片上傳</el-button>
       9           <div slot="tip" class="el-upload__tip">主要用于測試單文件分片上傳</div>
      10         </el-upload>
      11         <el-progress :text-inside="true" class="progress" :stroke-width="26" :percentage="singlePartFileProgress" />
      12       </div>
      13       <!-- 多文件分片上傳 -->
      14       <div class="fileUploadStyle">
      15         <h3>多文件分片上傳</h3>
      16         <el-upload ref="upload" name="files" action="#" :on-change="selectMultiplePartFile"
      17           :on-remove="removeMultiplePartFile" :file-list="multipleFilePart.fileList" :auto-upload="false">
      18           <el-button slot="trigger" size="small" type="primary">選取文件</el-button>
      19           <el-button style="margin-left: 10px;" size="small" type="success"
      20             @click="multipleFilePartUpload">點擊進行多文件分片上傳</el-button>
      21           <div slot="tip" class="el-upload__tip">主要用于測試多文件分片上傳</div>
      22         </el-upload>
      23       </div>
      24       <!-- 多文件(分片)秒傳 -->
      25       <div class="fileUploadStyle">
      26         <h3>多文件(分片MD5值)秒傳</h3>
      27         <el-upload ref="upload" name="files" action="#" :on-change="selectMultiplePartFlashFile"
      28           :on-remove="removeMultiplePartFlashFile" :file-list="multipleFilePartFlash.fileList" :auto-upload="false">
      29           <el-button slot="trigger" size="small" type="primary">選取文件</el-button>
      30           <el-button style="margin-left: 10px;" size="small" type="success"
      31             @click="multipleFilePartFlashUpload">點擊進行文件秒傳</el-button>
      32           <div slot="tip" class="el-upload__tip">主要用于測試多文件(分片MD5值)秒傳</div>
      33         </el-upload>
      34       </div>

      js屬性定義:

       上傳部分代碼:

       minio分片上傳:

       上傳樣式:

       

      • 功能演示及源碼

      部分演示圖: 這里就以上傳minio為例,測試上傳minio以分片方式上

       以8M進行分切 28M剛好分了四個區,我們使用redis客戶工具查看

       最后成功上傳到minio中

       

      而且看到上傳文件大小為:28M

      文件上傳代碼其實功能也簡單也很明確,先將一個大文件分成n個小文件,然后給后端檢測這些分片是否曾經上傳中斷過,即對這些分片進行過濾出來,并將過濾出對應的分片定位值結果返回給前端處理出不需要上傳的分片和需要上傳的文件分片,這里主要還是區分到確定是這個文件的分區文件。

      這里,為了方便大家直接能夠使用Java源碼,本文所有都采用Spring boot框架模式,另外使用了第三方插件,如果大家使用中沒有使用到minio可以不需要引入并把相關代碼移除即可,代碼使用了redis作為分區數量緩存,相對于Java內存更穩定些。

      demo源碼下載gitee地址(代碼包含Java后端工程和vue2前端工程):java-file-upload-demo: java 多文件上傳、多文件分片上傳、多文件秒傳、minio分片上傳等功能

       

       

       


       

      posted @ 2024-06-24 11:42  Angelasp  閱讀(3282)  評論(3)    收藏  舉報
      主站蜘蛛池模板: 亚洲国产99精品国自产拍| 国产无遮挡猛进猛出免费软件| 国产av成人精品播放| 国产精品亚欧美一区二区三区| 人妻少妇久久久久久97人妻 | 特级欧美AAAAAAA免费观看| 日韩精品国产中文字幕| 国产一区二区三区四区激情 | 国产国产午夜福利视频| 日韩av中文字幕有码| 亚洲国产高清第一第二区| 毛片亚洲AV无码精品国产午夜| 国产精品久久久久鬼色| 九九久久人妻一区精品色| 国模粉嫩小泬视频在线观看| 成av免费大片黄在线观看| 国产精品免费看久久久| 日韩激情无码免费毛片| 人妻少妇精品视频三区二区| 国产美女MM131爽爽爽| 亚洲夂夂婷婷色拍ww47| 国产毛片精品av一区二区| 亚洲国产成人精品女人久久久| 久久久国产成人一区二区| 国产欧美一区二区精品性色| 在线永久看片免费的视频| 人人人澡人人肉久久精品| 湘阴县| 亚洲日本中文字幕乱码中文| 99久久久国产精品消防器材| 国产精品亚洲А∨天堂免下载| 少妇真人直播免费视频| 国产色悠悠综合在线观看| 精品一区二区中文字幕| 亚洲人ⅴsaⅴ国产精品| 国产精品白丝一区二区三区| 88国产精品视频一区二区三区| 精品九九人人做人人爱| 亚洲中文字字幕精品乱码| 中文国产不卡一区二区| 国产精品一区 在线播放|