大文件断点续传解决方案:基于Vue 2与Spring Boot的完整实现
在现代Web应用中,大文件上传是一个常见但具有挑战性的需求。传统的文件上传方式在面对网络不稳定、大文件传输时往往表现不佳。本文将详细介绍如何实现一个支持断点续传的大文件上传功能,结合Vue 2前端和Spring Boot后端技术。
一、问题背景与挑战
在实际项目中,我们经常遇到需要上传大文件的场景,如视频、设计图纸、数据库备份等。这些文件可能达到几个GB甚至更大,直接使用传统表单上传方式会面临以下问题:
- 网络不稳定:上传过程中网络中断导致前功尽弃
- 服务器压力:大文件上传占用服务器资源时间长
- 用户体验:用户无法暂停/继续上传,进度不透明
- 重复上传:同一文件多次上传浪费带宽和存储空间
二、解决方案概述
我们的断点续传解决方案基于以下核心技术:
- 文件分片:将大文件分割成固定大小的块(如2MB)
- 唯一标识:使用MD5哈希值作为文件唯一标识
- 分片上传:仅上传服务器缺失的分片
- 状态记录:使用Redis记录已上传分片信息
- 合并恢复:所有分片上传完成后在服务器端合并
三、系统架构设计
前端架构(Vue 2.6.10)
- 文件选择组件
- MD5计算模块
- 分片管理模块
- 上传控制模块(开始/暂停/继续/取消)
- 进度显示组件
后端架构(Spring Boot)
- 文件状态检查接口
- 分片上传接口
- 分片合并接口
- 上传取消接口
- Redis存储服务
- 定时清理任务
四、核心技术实现
1. 前端核心代码
// 文件分片处理
createFileChunks() {if (!this.file) returnthis.uploadChunks = []const chunkCount = Math.ceil(this.file.size / CHUNK_SIZE)for (let i = 0; i < chunkCount; i++) {const start = i * CHUNK_SIZEconst end = Math.min(start + CHUNK_SIZE, this.file.size)const chunk = this.file.slice(start, end)this.uploadChunks.push({index: i,chunk: chunk,uploaded: this.uploadedChunkIndexes.includes(i),retries: 0})}
}// 带重试机制的分片上传
async uploadChunkWithRetry(chunk, maxRetries = 3) {try {await this.uploadChunk(chunk)chunk.uploaded = truethis.uploadedSize += chunk.chunk.size} catch (error) {chunk.retries++if (chunk.retries <= maxRetries) {await new Promise(resolve => setTimeout(resolve, 1000 * chunk.retries))return this.uploadChunkWithRetry(chunk, maxRetries)} else {throw error}}
}
2. 后端核心代码
2.1 Redis配置类
@Configuration
@EnableCaching
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper mapper = new ObjectMapper();mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);serializer.setObjectMapper(mapper);template.setValueSerializer(serializer);template.setKeySerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(2)) // 设置缓存有效期2小时.disableCachingNullValues();return RedisCacheManager.builder(factory).cacheDefaults(config).build();}
}
2.2 实体类
@Data
public class CancelUploadRequest {private String md5;
}@Data
public class CancelUploadResponse {private boolean success;private String message;
}@Data
public class CheckFileResponse {private boolean uploaded;private List<Integer> uploadedChunks;
}@Data
public class MergeChunksRequest {private String md5;private String fileName;private int totalChunks;private long fileSize;
}@Data
public class MergeChunksResponse {private boolean success;private String message;private String filePath;
}@Data
public class UploadChunkResponse {private boolean success;private String message;
}
2.3 核心实现
@Sl