Springboot大文件上传下载实现思路

文章介绍了如何实现文件的分片上传,通过计算每个分片的MD5值存储到数据库中以确保完整性。在上传过程中支持断点续传,记录已上传的分片和字节数,从上次中断的位置继续。下载时,文件会分段下载并合并成完整文件。

思路


  1. 将文件切割成多个分片。

  1. 对每个分片进行MD5计算,存储到数据库中,作为唯一标识。

  1. 上传时将每个分片上传到服务器,存储到指定的目录中。

  1. 完成上传时,根据MD5值将所有分片合并成一个完整的文件。

  1. 下载时,将文件分段下载,并进行文件合并操作,最终得到完整的文件。

分片实现思路


  1. 计算文件MD5值,作为唯一标识。

  1. 根据文件大小和指定的分片大小,计算文件需要被切割成多少个分片。

  1. 对每个分片进行MD5计算。

  1. 将分片的MD5值存储到数据库中。

断点续传实现思路


  1. 在上传文件时,记录已上传的分片,以及已经上传的字节数。

  1. 在继续上传时,先检查数据库中已上传的分片,然后从上次中断的地方继续上传。

代码实现


分片上传

@PostMapping("/upload")publicvoidupload(@RequestParam("file") MultipartFile file,
                        @RequestParam("index")int index,
                        @RequestParam("md5") String md5)throws Exception {

    // 获取文件存储路径
    String path = getFilePath(md5, index);

    // 将分片写入指定路径
    File dest = new File(path);
    file.transferTo(dest);

    // 计算分片MD5并存储到数据库
    String chunkMd5 = DigestUtils.md5Hex(new FileInputStream(dest));
    chunkService.saveChunk(md5, chunkMd5, index, dest.length());
}

分片合并

@SneakyThrows@PostMapping("/merge")publicvoidmerge(@RequestBody MergeRequest mergeRequest){
    File file = new File(getFilePath(mergeRequest.getMd5(), null));
    RandomAccessFile accessFile = new RandomAccessFile(file, "rw");

    for (int i = 0; i < mergeRequest.getTotalChunks(); i++) {
        File chunkFile = new File(getFilePath(mergeRequest.getMd5(), i));
        RandomAccessFile chunkAccessFile = new RandomAccessFile(chunkFile, "r");

        byte[] buffer = newbyte[1024];
        int len;
        while ((len = chunkAccessFile.read(buffer)) != -1) {
            accessFile.write(buffer, 0, len);
        }

        chunkAccessFile.close();
        chunkFile.delete();
    }

    accessFile.close();
}

分段下载

@GetMapping("/download")publicvoiddownload(HttpServletRequest request, HttpServletResponse response)throws Exception {
    String md5 = request.getParameter("md5");
    File file = new File(getFilePath(md5, null));

    if (!file.exists()) {
        thrownew RuntimeException("文件不存在");
    }

    response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());

    try (FileInputStream inputStream = new FileInputStream(file);
         ServletOutputStream outputStream = response.getOutputStream()) {

        byte[] bytes = newbyte[1024];
        int len;
        while ((len = inputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0, len);
        }
    }
}

断点续传

@PostMapping("/upload-resume")publicvoiduploadResume(@RequestParam("file") MultipartFile file,
                         @RequestParam("index") Integer index,
                         @RequestParam("md5") String md5,
                         HttpServletRequest request)throws Exception {
    // 判断该分片是否已经上传if (chunkService.isAlreadyUpload(md5, index)) {
        return;
    }

    // 获取文件存储路径
    String path = getFilePath(md5, index);

    // 判断是否需要续传long uploadedBytes = 0;
    File dir = new File(getFilePath(md5, null));
    File[] files = dir.listFiles();
    if (files != null) {
        List<File> fileList = Arrays.asList(files);
        Collections.sort(fileList, Comparator.comparing(File::getName));
        String lastFileName = fileList.get(fileList.size() - 1).getName();
        if (lastFileName.startsWith(index + "-", 32)) {
            uploadedBytes = Long.parseLong(lastFileName.split("-")[1]);
        }
    }

    // 将分片写入指定路径
    File dest = new File(path);
    long fileLength = file.getSize();
    RandomAccessFile accessFile = new RandomAccessFile(dest, "rw");
    accessFile.seek(uploadedBytes);
    try (InputStream inputStream = file.getInputStream()) {
        byte[] buffer = newbyte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            accessFile.write(buffer, 0, len);
            uploadedBytes += len;

            // 记录已上传的分片和字节数
            chunkService.saveResume(md5, index, uploadedBytes);
        }
    }
    accessFile.close();
}

注意事项


  1. 文件分片大小应该根据实际情况做出合理的设置,一般建议设置为2MB ~ 10MB。过小会造成太多分片,增加上传和合并的复杂度;过大会占用太多临时空间,且上传时易出现超时等问题。

  1. 对于较大的文件,应该避免在内存中进行操作,可以使用随机访问文件(RandomAccessFile)进行操作。

  1. 断点续传时,要对已经上传的分片进行排重,以避免重复上传。同时,要记录已上传的字节数,以便进行断点续传操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eddie_920

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值