专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

Spring Boot 如何实现大文件分块上传?

ins518 2024-10-03 00:04:26 技术文章 9 ℃ 0 评论

实现大文件的分块上传需要客户端和服务器的共同配合来实现,所谓的大文件分块上传是指将大文件拆分成多个小块分别进行上传操作,然后在服务器端将这些小块逐步接收并最终合并为完整的文件。通过这种方式可以有效避免单次上传时间过长或因为网络问题导致上传失败的问题。

实现流程

客户端分块上传流程

客户端将文件拆分为多个块,然后按照顺序对文件块进行上传,通常需要将文件拆分成几个块。而这些块需要包含如下的一些关键信息。

  • 文件标识 (fileIdentifier):用来标识这个文件
  • 块序号 (chunkNumber):当前块的编号,表示第几块。
  • 总块数 (totalChunks):表示文件被分成了多少块。
  • 块大小 (chunkSize):每块文件的大小,便于校验。

服务端处理分块上传

服务端需要处理每个块的上传请求,每个请求都会携带一个文件块,服务器需要将这些块暂时保存起来。当所有的块上传完成后服务器会将这些块合并为一个完整的文件。然后通过文件的标识(例如文件的哈希值)来校验最终合并的文件是否完整。

具体实现操作

定义上传接口

首先,定义文件上传接口,通过 @PostMapping 来接收上传请求。

@RestController
@RequestMapping("/upload")
public class FileUploadController {

    private static final String UPLOAD_DIR = "/tmp/uploads/";

    @PostMapping("/chunk")
    public ResponseEntity<String> uploadChunk(
        @RequestParam("fileIdentifier") String fileIdentifier,  // 文件唯一标识
        @RequestParam("chunkNumber") int chunkNumber,           // 当前块编号
        @RequestParam("totalChunks") int totalChunks,           // 块总数
        @RequestParam("file") MultipartFile file) throws IOException {

        // 创建保存分块的目录
        File uploadDir = new File(UPLOAD_DIR + fileIdentifier);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }

        // 保存当前的文件块
        File chunkFile = new File(uploadDir, chunkNumber + ".part");
        file.transferTo(chunkFile);

        // 如果所有块都已经上传完毕,则进行合并
        if (checkAllChunksUploaded(fileIdentifier, totalChunks)) {
            mergeChunks(fileIdentifier, totalChunks);
        }

        return ResponseEntity.ok("Chunk uploaded successfully");
    }

    // 判断是否所有块已经上传完毕
    private boolean checkAllChunksUploaded(String fileIdentifier, int totalChunks) {
        File uploadDir = new File(UPLOAD_DIR + fileIdentifier);
        for (int i = 1; i <= totalChunks; i++) {
            File chunkFile = new File(uploadDir, i + ".part");
            if (!chunkFile.exists()) {
                return false;
            }
        }
        return true;
    }

    // 合并所有文件块
    private void mergeChunks(String fileIdentifier, int totalChunks) throws IOException {
        File uploadDir = new File(UPLOAD_DIR + fileIdentifier);
        File mergedFile = new File(UPLOAD_DIR, fileIdentifier + ".merged");
        try (FileOutputStream fos = new FileOutputStream(mergedFile)) {
            for (int i = 1; i <= totalChunks; i++) {
                File chunkFile = new File(uploadDir, i + ".part");
                Files.copy(chunkFile.toPath(), fos);
                chunkFile.delete();  // 合并后删除分块文件
            }
        }
        // 删除临时目录
        uploadDir.delete();
    }
}

客户端文件分块上传

在客户端这边我们可以通过文件上传的方式来进行调用,相关接口实现文件的上传操作,如下所示。

<input type="file" id="fileInput" />
<script>
  const fileInput = document.getElementById('fileInput');

  fileInput.addEventListener('change', async () => {
    const file = fileInput.files[0];
    const chunkSize = 2 * 1024 * 1024; // 每块 2MB
    const totalChunks = Math.ceil(file.size / chunkSize);

    for (let chunkNumber = 0; chunkNumber < totalChunks; chunkNumber++) {
      const start = chunkNumber * chunkSize;
      const end = Math.min(start + chunkSize, file.size);
      const chunk = file.slice(start, end);
      const formData = new FormData();
      
      formData.append('fileIdentifier', file.name); // 使用文件名作为文件标识
      formData.append('chunkNumber', chunkNumber + 1);  // 从1开始编号
      formData.append('totalChunks', totalChunks);
      formData.append('file', chunk);

      await fetch('/upload/chunk', {
        method: 'POST',
        body: formData
      });
    }
    alert('File uploaded successfully');
  });
</script>

需要注意

在进行文件分块上传的时候,需要注意如下几个问题。

  • 文件块的校验:可以通过对每个块生成 MD5 校验码,上传时进行验证,确保块传输过程没有错误。
  • 文件断点续传:如果中断上传,可以根据文件标识与当前已上传的块,继续从断点位置开始上传。
  • 文件并发上传:前端可以并行上传多个块以提高上传速度,服务器端可以通过并发处理来提升性能。

通过以上的方式,我们就可以实现文件分块上传,这种文件处理方式适用于处理大文件的上传,尤其是在不稳定的网络环境下,能够有效提高上传的成功率和效率。在实际场景中我们需要注意上面提到的几点注意。结合实际情况对文件分片,校验,端点续传等功能进行优化开发

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表