文件上传——存储上传进度解决方案及断点续传

10 篇文章 0 订阅
8 篇文章 0 订阅

为了在上传意外中断后能够恢复进度,需要将上传进度持久化存储。以下介绍两种常用的方案:

方案一:使用数据库存储上传进度

  1. 数据库设计:

    • 创建一个 upload_progress 表,包含以下字段:
      • id: 主键,自增 ID
      • identifier: 全局唯一标识符,例如 UUID,用于标识一个上传任务
      • file_name: 文件名
      • total_chunks: 总分片数
      • uploaded_chunks: 已上传的分片索引列表,可以使用 JSON 字符串存储
  2. 代码实现:

    • 创建实体类:
    import javax.persistence.*;
    import java.util.List;
    
    @Entity
    @Table(name = "upload_progress")
    public class UploadProgress {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(unique = true)
        private String identifier;
    
        private String fileName;
    
        private int totalChunks;
    
        @ElementCollection
        @CollectionTable(name = "uploaded_chunks", joinColumns = @JoinColumn(name = "progress_id"))
        @Column(name = "chunk_index")
        private List<Integer> uploadedChunks;
    
        // 构造函数、Getter 和 Setter 方法
    }
    
    • 注入 UploadProgressRepository:
    @Autowired
    private UploadProgressRepository uploadProgressRepository;
    
    • 修改 saveChunkToFile 方法:
    private void saveChunkToFile(MultipartFile file, int chunkIndex, int totalChunks, String fileName, String identifier) throws IOException {
        // ... 保存分片文件 ...
    
        // 更新上传进度到数据库
        UploadProgress progress = uploadProgressRepository.findByIdentifier(identifier)
                .orElseGet(() -> new UploadProgress(identifier, fileName, totalChunks, new ArrayList<>()));
        progress.getUploadedChunks().add(chunkIndex);
        uploadProgressRepository.save(progress);
    
        // 检查所有分片是否已上传完成
        if (progress.getUploadedChunks().size() == totalChunks) {
            // ... 合并分片 ...
            // 清除上传进度
            uploadProgressRepository.delete(progress);
        }
    }
    
    • 修改 /upload/progress 接口:
    @GetMapping("/upload/progress")
    public ResponseEntity<UploadResponse> getUploadProgress(
            @RequestParam("identifier") String identifier
    ) {
        UploadProgress progress = uploadProgressRepository.findByIdentifier(identifier).orElse(null);
        if (progress != null) {
            return ResponseEntity.ok(new UploadResponse(progress.getUploadedChunks()));
        } else {
            return ResponseEntity.ok(new UploadResponse(Collections.emptyList()));
        }
    }
    

方案二:使用 Redis 存储上传进度

  1. Redis 数据结构:

    • 使用 Hash 结构存储每个文件的上传进度,key 为 identifier,field 为分片索引,value 为 true 或 false,表示分片是否已上传。
  2. 代码实现:

    • 注入 RedisTemplate:
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    • 修改 saveChunkToFile 方法:
    private void saveChunkToFile(MultipartFile file, int chunkIndex, int totalChunks, String fileName, String identifier) throws IOException {
        // ... 保存分片文件 ...
    
        // 更新上传进度到 Redis
        String chunkKey = identifier + ":" + chunkIndex;
        redisTemplate.opsForValue().set(chunkKey, "true");
    
        // 检查所有分片是否已上传完成
        if (redisTemplate.opsForHash().size(identifier) == totalChunks) {
            // ... 合并分片 ...
            // 清除上传进度
            redisTemplate.delete(identifier);
        }
    }
    
    • 修改 /upload/progress 接口:
    @GetMapping("/upload/progress")
    public ResponseEntity<UploadResponse> getUploadProgress(
            @RequestParam("identifier") String identifier
    ) {
        Set<String> uploadedChunks = redisTemplate.keys(identifier + ":*");
        Set<Integer> uploadedChunkIndices = uploadedChunks.stream()
                .map(s -> Integer.parseInt(s.substring((identifier + ":").length())))
                .collect(Collectors.toSet());
        return ResponseEntity.ok(new UploadResponse(uploadedChunkIndices));
    }
    

重新上传获取进度:

  • 用户重新选择同一个文件上传时,需要生成相同的 identifier
  • 在前端上传前,调用 /upload/progress 接口,传入 identifier 获取已上传的分片信息。
  • 根据返回的已上传分片信息,跳过已上传的分片,继续上传剩余分片。

选择方案的建议:

  • 如果上传文件较多,且需要持久化存储上传进度,建议使用数据库方案。
  • 如果追求更高的性能,且上传进度信息不需要永久保存,可以考虑使用 Redis 方案。

无论选择哪种方案,都需要确保 identifier 的唯一性,以便准确地标识一个上传任务,实现断点续传功能。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值