1. 为什么需要分片上传与断点续传在 Web 应用中上传大文件如视频、安装包、数据集时如果采用传统的单次上传方式往往会遇到两个突出问题一是网络波动导致上传中断后必须从头重传浪费时间和带宽二是单个请求体过大容易触发网关超时或服务器内存压力。分片上传和断点续传正是为了解决这两个问题而设计的。分片上传的核心思路是把一个大文件切成多个较小的分片逐个上传到服务器断点续传则是在上传中断后能够从已上传的分片之后继续而不是重新上传整个文件。两者通常配合使用共同提升大文件上传的稳定性和效率。2. 整体流程设计一个完整的断点续传与分片上传流程通常包含以下几个阶段初始化上传前端向后端发起创建上传任务的请求携带文件名、文件大小、分片大小等信息后端返回一个唯一的上传任务 ID。计算分片信息前端根据分片大小将文件切分为多个分片并为每个分片生成序号和校验值如 MD5。逐片上传前端按顺序或并发将每个分片上传到后端后端保存分片并记录已上传状态。查询已上传分片上传中断后前端向后端查询该任务已成功上传的分片列表据此跳过已完成的分片。合并文件所有分片上传完成后前端通知后端执行合并操作后端将分片按顺序拼接为完整文件。3. 前端实现要点前端主要负责文件切片、上传调度、进度展示和断点恢复。下面以 JavaScript 为例说明核心实现思路。3.1 文件切片利用 HTML5 的 File API可以通过slice方法将文件切分为多个 Blob 分片const CHUNK_SIZE 5 * 1024 * 1024; // 每个分片 5MB function createChunks(file) { const chunks []; let start 0; while (start file.size) { const end Math.min(start CHUNK_SIZE, file.size); chunks.push(file.slice(start, end)); start end; } return chunks; }3.2 上传分片每个分片通过独立的请求上传并携带任务 ID 和分片序号便于后端识别和排序async function uploadChunk(taskId, chunk, index) { const formData new FormData(); formData.append(taskId, taskId); formData.append(index, index); formData.append(chunk, chunk); const response await fetch(/api/upload/chunk, { method: POST, body: formData }); return response.json(); }3.3 断点续传上传中断后重新开始时先查询已上传的分片只上传缺失的部分async function resumeUpload(taskId, chunks) { const res await fetch(/api/upload/status?taskId${taskId}); const { uploadedIndexes } await res.json(); const tasks chunks.map((chunk, index) { if (uploadedIndexes.includes(index)) { return Promise.resolve({ index, skipped: true }); } return uploadChunk(taskId, chunk, index).then(() ({ index, skipped: false })); }); await Promise.all(tasks); await mergeFile(taskId); }4. 后端实现要点后端负责接收分片、记录上传状态、校验完整性以及合并文件。下面以 Java Spring Boot 为例说明核心接口设计。4.1 初始化上传任务PostMapping(/api/upload/init) public MapString, Object initUpload(RequestBody InitUploadRequest request) { String taskId UUID.randomUUID().toString(); // 保存任务信息文件名、总大小、分片大小、分片总数 uploadTaskService.createTask(taskId, request); return Map.of(taskId, taskId, chunkSize, request.getChunkSize()); }4.2 接收分片PostMapping(/api/upload/chunk) public ResponseEntity? uploadChunk( RequestParam String taskId, RequestParam int index, RequestParam MultipartFile chunk) { // 校验任务存在保存分片到临时目录 uploadTaskService.saveChunk(taskId, index, chunk); return ResponseEntity.ok().build(); }4.3 查询已上传分片GetMapping(/api/upload/status) public MapString, Object getUploadStatus(RequestParam String taskId) { ListInteger uploadedIndexes uploadTaskService.getUploadedIndexes(taskId); return Map.of(uploadedIndexes, uploadedIndexes); }4.4 合并分片PostMapping(/api/upload/merge) public ResponseEntity? merge(RequestParam String taskId) { uploadTaskService.mergeChunks(taskId); return ResponseEntity.ok().build(); }5. 关键细节与注意事项分片大小选择分片过小会导致请求数量过多增加网络开销分片过大会失去分片上传的优势。一般建议 1MB 到 10MB 之间可根据网络环境动态调整。并发控制并发上传多个分片可以提升速度但需要控制并发数避免占用过多连接或触发服务端限流。完整性校验每个分片上传时可携带 MD5 值服务端校验后存储合并后也可对整体文件做一次校验确保数据完整。任务过期清理长时间未完成的上传任务应设置过期时间定期清理临时分片避免占用磁盘空间。秒传支持在初始化时计算整个文件的 MD5若服务端已存在相同文件可直接返回秒传结果跳过实际上传。6. 总结断点续传和分片上传的实现核心在于将大文件拆分为独立可管理的小分片并通过任务状态记录实现中断后的精准续传。前端负责切片和调度后端负责存储、状态管理和合并两者配合即可显著提升大文件上传的稳定性和用户体验。实际项目中还可以结合并发控制、完整性校验和秒传机制进一步优化上传效率。