大文件斷點續傳解決方案:基于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