文件断点续传(断点上传)完整实现方案(后端视角)
适用场景:大文件上传、弱网环境、移动端上传、失败可恢复、避免重复上传
技术栈示例:HTTP + Java + Redis + MySQL + 对象存储(OSS / COS / S3)
一、什么是文件断点续传
断点续传 = 文件分片上传 + 状态记录 + 可恢复上传
核心思想:
- 把一个大文件切成多个 Chunk(分片)
- 每个分片单独上传
- 服务端记录已上传分片
- 中断后只补传缺失分片
一句话:上传失败 ≠ 重来,而是从"断点"继续
二、为什么一定要用断点上传
1️⃣ 解决的问题
| 问题 | 普通上传 | 断点上传 |
|---|---|---|
| 大文件 | ❌ 容易失败 | ✅ 稳定 |
| 网络抖动 | ❌ 重来 | ✅ 续传 |
| 用户体验 | ❌ 崩溃 | ✅ 友好 |
| 带宽浪费 | ❌ 严重 | ✅ 可控 |
2️⃣ 典型场景
- 视频 / 音频 / 安装包
- 企业网盘
- IM 文件发送
- 后台管理系统上传 Excel / 压缩包
三、整体架构设计(后端视角)
Client
↓(分片)
Upload API
↓
Redis(上传状态)
↓
临时分片存储(磁盘 / OSS)
↓
合并服务
↓
最终文件存储(OSS / 本地)
关键点:
- Redis:记录分片上传进度
- OSS:避免服务端磁盘爆炸
- 合并操作:必须保证原子性
四、核心概念定义
1️⃣ 文件唯一标识(fileId)
通常使用:
fileId = MD5(文件内容)
作用:
- 秒传(已存在直接返回)
- 断点识别
- 去重
2️⃣ 分片参数
| 参数 | 含义 |
|---|---|
| chunkIndex | 当前分片索引(从 0 开始) |
| chunkSize | 分片大小(如 5MB) |
| totalChunks | 总分片数 |
| fileId | 文件唯一标识 |
五、接口设计(非常关键)
1️⃣ 初始化上传
接口
POST /upload/init
请求
json
{
"fileId": "md5值",
"fileName": "test.zip",
"fileSize": 10240000,
"chunkSize": 5242880
}
返回
json
{
"uploadedChunks": [0,2,3]
}
👉 客户端只上传缺失分片
2️⃣ 上传分片
POST /upload/chunk
参数
- fileId
- chunkIndex
- MultipartFile chunk
服务端逻辑
- 校验分片合法性
- 保存分片
- Redis 记录已上传 chunkIndex
3️⃣ 合并分片
POST /upload/merge
流程
- 校验所有分片是否齐全
- 按顺序合并
- 生成最终文件
- 清理分片 & Redis
六、Redis 设计(重点)
1️⃣ Key 设计
upload:{fileId}
2️⃣ Value 结构
text
Set<Integer> uploadedChunks
或:
text
Bitmap(更省内存)
3️⃣ 为什么用 Redis
- 高并发
- 原子操作
- 进度查询快
- 自动过期
七、Java 后端核心代码示例
1️⃣ 保存分片
java
String key = "upload:" + fileId;
redisTemplate.opsForSet().add(key, chunkIndex);
2️⃣ 校验是否上传完成
java
Long count = redisTemplate.opsForSet().size(key);
if (count == totalChunks) {
// 允许合并
}
八、分片合并的坑(一定要看)
❌ 常见错误
- 合并过程中进程挂了
- 多个请求同时触发 merge
- 合并顺序错乱
✅ 正确做法
-
分布式锁
lock:merge:{fileId}
-
顺序合并
chunk_0 → chunk_1 → chunk_2
-
临时文件 + rename
rename 在 Linux 下是原子操作
九、秒传是怎么实现的
流程:
- 客户端计算 MD5
- init 接口检查数据库
- 已存在 → 直接返回 URL
java
if (fileExists(fileId)) {
return "秒传成功";
}
本质:去重,不是快,是不传
十、断点上传 vs 对象存储直传
| 方案 | 特点 |
|---|---|
| 后端中转 | 控制力强 |
| OSS 分片上传 | 性能最好 |
| OSS + 回调 | 企业级最优解 |
强烈建议:
大文件 = OSS 分片上传 + 后端校验
十一、生产级优化建议
- 分片大小:5MB ~ 10MB
- Redis Key 设置过期时间
- 合并操作异步化
- 上传限流(防止刷接口)
- 上传鉴权(防止任意文件)