一、文件“秒传” & “断点续传”实现原理(面试级一句话 + 流程图)
1. 秒传(极速完成)
核心:“不用传,只认指纹”
- 前端计算文件整体 SHA-256(或 MD5+文件大小)→ 得到 fileHash
- 上传前先发
POST /check-hashjson{"fileHash":"a8f2...","size":2147483648} - 服务端在 文件表 查 hash 是否存在
- ✅ 存在:直接返回 文件 URL → 前端提示“秒传成功”
- ❌ 不存在:走正常上传流程(普通 / 断点)
额外加速:
- 秒传 + 分片 hash 索引:每个分片也算 hash,秒传失败时可直接复用已上传分片(秒传失败 → 秒级断点续传)
2. 断点续传(大文件不怕中断)
- 分片:前端固定 4 MB 一块(可配置)
- 顺序编号:
chunkIndex = 0..N-1 - 并发上传:浏览器限制 6 并发,HTTP/2 可 100+
- 校验:每片带 chunkHash,服务端校验 CRC32 确认无误再合并
- 记录进度:Redis / MySQL 存
upload:fileHash -> BitSet(已上传分片),重启后秒级恢复
合并流程
text
所有分片上传完 → 服务端多线程顺序写入 → 生成最终文件 → 再次校验整体 hash → 返回 URL → 清理分片临时目录秒传 + 断点组合时序
前端:计算 fileHash → check-hash → 秒传?
├─ 成功 → 完成
└─ 失败 → 分片上传(带 chunkHash)→ 中途断网 → 重新进入上传 → BitSet 告诉前端“已有 0,1,3 片”→ 只传缺失片 → 合并 → 完成代码片段(Spring 部分)
java
@PostMapping("/chunk")
public ApiResp chunkUpload(@RequestParam MultipartFile file,
@RequestParam String fileHash,
@RequestParam int chunkIndex){
String tmpDir = "/tmp/" + fileHash;
File chunkFile = new File(tmpDir, chunkIndex + ".part");
file.transferTo(chunkFile);
// 更新 BitSet
RBitSet bitSet = redisson.getBitSet("upload:" + fileHash);
bitSet.set(chunkIndex);
if (bitSet.cardinality() == totalChunks) { // 全部到齐
File target = new File("/data", fileHash);
mergeChunks(tmpDir, target);
if (sha256(target).equals(fileHash))
return ApiResp.ok(target.getName());
else throw new RuntimeException("合并后 hash 不一致");
}
return ApiResp.accept();
}