JAVA实现大文件分片上传断点续传

直接上代码

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("\\")){
        String replaceFileName = fileName.replaceAll("\\\\", "/");
        int lastIndexOf = replaceFileName.lastIndexOf("/");
        if(lastIndexOf > 0) {
          newFileName = replaceFileName.substring(lastIndexOf);
        }
      }

     int lastIndexOf = newFileName.lastIndexOf(".");
        String fileSuffix = lastIndexOf >= 0 ? newFileName.substring(lastIndexOf + 1).toLowerCase() : "";
        fileSuffix = newFileName.toLowerCase().endsWith(".tar.gz") ? "tar.gz" : fileSuffix;
        return fileSuffix;
    }
}

 

posted @ 2024-03-17 09:02  bug毁灭者  阅读(11)  评论(0编辑  收藏  举报