10 KiB
10 KiB
断点续传流程图:前端IndexedDB vs 后端内存Map/Redis
🔄 完整上传流程
┌─────────────────────────────────────────────────────────────────┐
│ 断点续传完整流程 │
└─────────────────────────────────────────────────────────────────┘
用户操作 前端(IndexedDB) 后端(内存Map/Redis)
─────────────────────────────────────────────────────────────────
1. 选择文件
└─> 生成fileId
└─> 检查IndexedDB
└─> 没有记录 → 新上传
2. 初始化上传
└─> 调用 /api/upload/init
└─> └─> 创建uploadSession
└─> 保存到内存Map/Redis
└─> 返回uploadId
└─> 保存到IndexedDB
fileId, uploadId, uploadedChunks: []
3. 上传块1
└─> uploadChunk(uploadId, 0, chunk1)
└─> └─> 验证uploadSession
└─> 保存块文件到磁盘
└─> 更新uploadSession.chunks
└─> chunks[0] = "/path/to/chunk-0"
└─> 更新IndexedDB
uploadedChunks: [0]
4. 上传块2
└─> uploadChunk(uploadId, 1, chunk2)
└─> └─> 验证uploadSession
└─> 保存块文件到磁盘
└─> 更新uploadSession.chunks
└─> chunks[1] = "/path/to/chunk-1"
└─> 更新IndexedDB
uploadedChunks: [0, 1]
5. ... (继续上传其他块)
6. 完成上传
└─> completeUpload(uploadId)
└─> └─> 从uploadSession读取所有块路径
└─> 合并所有块文件
└─> 创建数据库记录
└─> 删除uploadSession
└─> 清理临时文件
└─> 删除IndexedDB记录
🔄 页面刷新恢复流程
┌─────────────────────────────────────────────────────────────────┐
│ 页面刷新恢复流程 │
└─────────────────────────────────────────────────────────────────┘
用户操作 前端(IndexedDB) 后端(内存Map/Redis)
─────────────────────────────────────────────────────────────────
1. 刷新页面
└─> 重新加载文件
└─> 生成fileId(相同文件 → 相同fileId)
└─> 检查IndexedDB
└─> ✅ 找到记录
uploadId: "upload-xxx"
uploadedChunks: [0, 1, 2]
2. 恢复上传
└─> 跳过已上传块 [0, 1, 2]
└─> 从块3开始上传
└─> uploadChunk(uploadId, 3, chunk3)
└─> └─> 检查uploadSession
└─> ✅ 找到会话(如果后端数据还在)
└─> 保存块3
└─> 更新uploadSession.chunks
└─> 更新IndexedDB
uploadedChunks: [0, 1, 2, 3]
3. 继续上传剩余块
└─> 块4, 5, 6...
⚠️ 服务器重启场景对比
场景A:使用内存Map(当前实现)
时间线 前端(IndexedDB) 后端(内存Map)
─────────────────────────────────────────────────────────────────
T1: 上传块1-5
└─> uploadedChunks: [0,1,2,3,4] └─> chunks: {0,1,2,3,4}
T2: 服务器重启
└─> uploadedChunks: [0,1,2,3,4] └─> ❌ 数据丢失
T3: 继续上传块6
└─> uploadChunk(uploadId, 5, chunk5)
└─> └─> ❌ uploadSession不存在
└─> 返回错误:会话不存在
└─> ❌ 无法恢复
└─> 需要重新初始化上传
└─> 但之前的块文件还在临时目录
└─> ❌ 无法恢复,需要重新上传
场景B:使用Redis(生产环境推荐)
时间线 前端(IndexedDB) 后端(Redis)
─────────────────────────────────────────────────────────────────
T1: 上传块1-5
└─> uploadedChunks: [0,1,2,3,4] └─> chunks: {0,1,2,3,4}
└─> ✅ 持久化到磁盘
T2: 服务器重启
└─> uploadedChunks: [0,1,2,3,4] └─> ✅ 从磁盘恢复数据
└─> chunks: {0,1,2,3,4}
T3: 继续上传块6
└─> uploadChunk(uploadId, 5, chunk5)
└─> └─> ✅ 从Redis读取uploadSession
└─> ✅ 找到会话
└─> 保存块5
└─> 更新Redis
└─> ✅ 完美恢复
└─> 从50%继续上传
🔍 数据一致性检查
理想情况:两者数据一致
前端 IndexedDB 后端 内存Map/Redis
─────────────────────────────────────────────
uploadedChunks: [0,1,2] chunks: {0,1,2}
✅ 数据一致
✅ 可以正常继续上传
情况1:前端数据丢失
前端 IndexedDB 后端 内存Map/Redis
─────────────────────────────────────────────
❌ 数据被清除 chunks: {0,1,2}
✅ 数据还在
处理方案:
1. 前端重新初始化上传
2. 后端检测到uploadId已存在
3. 后端可以返回已上传的块列表
4. 前端可以跳过已上传的块
情况2:后端数据丢失(内存Map)
前端 IndexedDB 后端 内存Map
─────────────────────────────────────────────
uploadedChunks: [0,1,2] ❌ 数据丢失(重启)
处理方案:
1. 前端尝试上传块3
2. 后端返回错误:会话不存在
3. 前端检测到会话丢失
4. 前端重新初始化上传
5. ❌ 无法恢复,需要重新上传
情况3:后端数据丢失(Redis,未持久化)
前端 IndexedDB 后端 Redis
─────────────────────────────────────────────
uploadedChunks: [0,1,2] ❌ 数据丢失(重启,未持久化)
处理方案:
与内存Map相同,无法恢复
情况4:后端数据保留(Redis,已持久化)
前端 IndexedDB 后端 Redis
─────────────────────────────────────────────
uploadedChunks: [0,1,2] ✅ 数据保留(重启,已持久化)
chunks: {0,1,2}
处理方案:
1. 前端尝试上传块3
2. 后端从Redis读取会话
3. ✅ 找到会话,继续上传
4. ✅ 完美恢复
💡 关键理解
1. 前端IndexedDB的作用
- 记录上传进度 - 客户端知道哪些块已上传
- 页面刷新恢复 - 刷新后可以从断点继续
- 跳过已上传块 - 节省时间和带宽
- 用户体验 - 显示上传进度
2. 后端uploadSessions的作用
- 验证上传会话 - 确保上传请求合法
- 记录已接收块 - 服务器知道哪些块已收到
- 存储块文件路径 - 知道每个块文件存储位置
- 权限控制 - 确保用户只能访问自己的上传
- 合并文件 - 完成上传时知道如何合并
3. 两者的关系
- 前端记录进度 - 基于网络请求成功
- 后端记录会话 - 基于实际文件写入
- 双重保障 - 前端和后端都有记录
- 断点续传 - 两者配合实现完整功能
4. 内存Map vs Redis的影响
-
内存Map:
- 服务器重启 → 数据丢失 → ❌ 无法恢复
- 前端可以恢复,但后端无法验证
- 需要重新初始化上传
-
Redis:
- 服务器重启 → 数据保留 → ✅ 可以恢复
- 前端和后端都可以恢复
- 完美的断点续传体验
🎯 总结
断点续传 = 前端IndexedDB + 后端uploadSessions
-
前端IndexedDB:
- 负责客户端恢复(页面刷新、浏览器关闭)
- 记录上传进度
- 跳过已上传块
-
后端uploadSessions:
- 负责服务器验证(会话验证、权限控制)
- 记录已接收块
- 存储块文件路径
-
两者配合:
- 前端知道进度,后端知道状态
- 两者配合实现完整的断点续传
- 前端可以恢复,后端也可以恢复(使用Redis)
-
内存Map vs Redis:
- 内存Map:服务器重启后无法恢复
- Redis:服务器重启后可以恢复(如果配置持久化)
- 生产环境推荐使用Redis