直接上程式碼
import org.springframework.web.multipart.MultipartFile; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; import lombok.extern.slf4j.Slf4j; import java.text.DecimalFormat; import java.io.*; @Slf4j public class FileSliceUpload { // 檔案上傳地址 private final String uploadPath = "/data/upload/"; /** * @description: 檔案分片上傳 * @date: 2024/3/12 15:25 * @param fileSliceDTO * @return boolean */ public boolean fileUpload(FileSliceDTO fileSliceDTO) { // 當前分片序號 Integer chunkNumber = fileSliceDTO.getChunkNumber(); // 當前分片大小 Long currentChunkSize = fileSliceDTO.getCurrentChunkSize(); // 總分片數 Integer totalChunks = fileSliceDTO.getTotalChunks(); // 原檔案md5 String fileMd5 = fileSliceDTO.getFileMd5(); // 檔名稱 String fileName = fileSliceDTO.getFileName(); // 使用者賬號 String userAccount = fileSliceDTO.getUserAccount(); // 檔案總大小 Long totalSize = fileSliceDTO.getTotalSize(); // 當前分片檔案流 MultipartFile mFile = fileSliceDTO.getFile(); log.info("接收到檔案:{},總大小:{} 總分片:{} 當前分片:{}", fileName, this.readableFileSize(totalSize), totalChunks, chunkNumber); String path = uploadPath + fileMd5 + "_" + userAccount + File.separator; File dirfile = new File(path); if (!dirfile.exists()) { dirfile.mkdirs(); } String currentChunkFileName = path + fileMd5 + "_" + chunkNumber + ".tmp"; File file = new File(currentChunkFileName); boolean uploadFlag = this.fileUpload(mFile, file); if (uploadFlag) { if (chunkNumber < totalChunks) { return true; } else { // 如果是最後一個分片,上傳成功後就進行檔案合併 String mergeFileName = null; try { mergeFileName = this.fileMerge(fileMd5, totalChunks, fileName, totalSize, userAccount); } catch (IOException e) { log.info("檔案合併發生異常:{}, {}", fileName, e.toString()); } if (StringUtils.isBlank(mergeFileName)) { // 合併失敗後非同步刪除資料夾 CompletableFuture.runAsync(() - > { delFile(new File(path)); }); return false; } log.info("檔案合併成功:{}, 總大小: {}", fileName, this.readableFileSize(totalSize)); // 合併成功後非同步刪除資料夾 CompletableFuture.runAsync(() - > {delFile(new File(path)); }); return true; } } return false; } /** * @description: 檔案上傳私有方法 * @date: 2024/3/12 15:25 * @param multipartFile 分片檔案 * @param file 目標檔案 * @return boolean */ private boolean fileUpload(MultipartFile multipartFile, File file) { boolean flag = false; try { if (!file.exists()) { file.createNewFile(); multipartFile.transferTo(file); } else { log.info("當前分片檔案已存在:{}", file.getName()); } flag = true; } catch (Exception e) { log.info("檔案上傳失敗:{}, {}", multipartFile.getOriginalFilename(), e.toString()); } return flag; } /** * @description: 合併檔案 * @date: 2024/3/12 15:21 * @param fileMd5 * @param chunks * @param fileName * @param totalSize * @param userAccount * @return java.lang.String */ private String fileMerge(String fileMd5, Integer chunks, String fileName, long totalSize, String userAccount) throws IOException { String fileType = this.getFileSuffix(fileName); String mergePath = uploadPath + UUIDUtil.uuid32() + "." + fileType; FileOutputStream fileOutputStream = new FileOutputStream(mergePath); String mergeFileName = null; long fileSize = 0 L; try { byte[] buf = new byte[1024 * 4]; for (int i = 1; i <= chunks; i++) { String chunkFile = i + ".tmp"; File file = new File(uploadPath + fileMd5 + "_" + userAccount + File.separator + fileMd5 + "_" + chunkFile); fileSize = fileSize + file.length(); InputStream inputStream = new FileInputStream(file); int len; while ((len = inputStream.read(buf)) != -1) { fileOutputStream.write(buf, 0, len); } inputStream.close(); } mergeFileName = mergePath; } catch (Exception e) { log.info("合併檔案失敗:{},{}", fileName, e.toString()); } finally { fileOutputStream.close(); } if (fileSize != totalSize) { log.info("檔案總大小不一致:{}", fileName); return null; } return mergeFileName; } /** * @description: 刪除檔案/資料夾 * @date: 2024/3/12 15:19 * @param file * @return boolean */ private boolean delFile(File file) { if (file == null || !file.exists()) { return true; } if (!file.isDirectory()) { file.delete(); return true; } else { File[] files = file.listFiles(); for (File f: files) { if (f.isDirectory()) { delFile(f); } else { f.delete(); } } file.delete(); return true; } } /** * @description: 檔案大小可讀化轉換 * @date: 2024/3/12 15:18 * @param size * @return java.lang.String */ private String readableFileSize(long size) { if (size <= 0) { return "0"; } final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" }; int digitGroups = (int)(Math.log10(size) / Math.log10(1024)); return new DecimalFormat("#,###.##").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups]; } /** * @description: 獲取檔案的擴充名 支援形式 abc.jpg or D:/data/abc.txt; * @date: 2024/3/12 15:16 * @param fileName * @return java.lang.String */ private String getFileSuffix(String fileName) { String newFileName = fileName; if (fileName.contains("/") || fileName.contains("\\")) { File file = new File(fileName); if (file.exists()) { newFileName = file.getName(); } } int lastIndexOf = newFileName.lastIndexOf("."); String fileSuffix = lastIndexOf >= 0 ? newFileName.substring(lastIndexOf + 1).toLowerCase() : ""; fileSuffix = newFileName.toLowerCase().endsWith(".tar.gz") ? "tar.gz" : fileSuffix; return fileSuffix; } }