秒传必须基于内容一致性而非文件名和大小,核心是分块哈希(如每4MB一块SHA256)、客户端预计算并缓存指纹、服务端用布隆过滤器+Redis快速查重,上传后通过Merkle根比对与索引定位差异块。

大文件秒传为什么不能只靠文件名和大小判断
仅比对 os.Stat().Size 和文件名,会漏掉内容相同但修改时间不同、或被截断/填充过的文件。真实场景中,两个 2GB 的视频文件可能名字一样、大小一样,但一帧画面有差异——秒传必须基于内容一致性,而不是元数据。
核心思路是:客户端预计算文件指纹(如分块哈希),上传前查服务端是否已有该指纹;服务端需支持按块索引 + 快速比对。常见错误是直接用 md5.Sum(file) 整体哈希——10GB 文件读一遍再算,用户等 30 秒才开始“秒”不起来。
- 必须分块计算(例如每 4MB 一块),支持断点续算和并发加速
- 块哈希结果要可复用:同一块在不同文件中出现时,服务端能复用存储
- 客户端需缓存已计算块的哈希(避免重复计算),可用
map[string]bool存hex.EncodeToString(blockHash[:])
Go 中如何高效分块计算 SHA256 并支持断点
用 io.Copy 配合 hash.Hash 是基础,但关键在控制缓冲区与块边界。别直接 io.Copy(hash, file)——那还是整文件哈希。要手动按固定 size 切片读取,每块独立哈希。
示例片段(省略 error 处理):
立即学习“go语言免费学习笔记(深入)”;
const blockSize = 4 * 1024 * 1024 // 4MB
hashes := make([][32]byte, 0)
buf := make([]byte, blockSize)
for {
n, err := file.Read(buf)
if n > 0 {
h := sha256.Sum256(buf[:n])
hashes = append(hashes, h)
}
if err == io.EOF {
break
}
}注意:file.Read(buf) 不保证每次读满 blockSize,最后一块可能不足;buf 复用可避免频繁 alloc,但必须用 buf[:n] 截取真实长度,否则哈希结果错。
- 若文件总 size 不是
blockSize整数倍,最后一块必须单独处理,不能忽略 - 并发计算多块?用
sync.Pool管理sha256.Hash实例,避免sha256.New()频繁 malloc - 不要用
crypto/sha256的 streaming 接口拼接块哈希——那是为流式签名设计的,不是为分块校验
服务端如何快速响应“这块我有没有”查询
客户端上传前发一个 POST,body 是 JSON 数组: ["a1b2c3...", "d4e5f6...", ...],服务端得在毫秒级返回每个 hash 是否存在。不能每查一次就扫一遍磁盘或跑一次 SQL IN 查询。
推荐方案:内存映射 + 布隆过滤器(Bloom Filter)初筛 + Redis 缓存精确结果。布隆过滤器可大幅降低 DB 查询量(假阳性率设 0.1% 时,1GB 内存可存 10 亿 hash);Redis key 设为 chunk:sha256hex,value 可为空或存块所在文件 ID。
- Redis TTL 建议设长一点(如 7 天),冷 chunk 自动过期不影响热数据
- 避免把整个哈希数组塞进一个 Redis key——查单个 hash 时得反序列化全部,用
EXISTS或MGET批量查更高效 - 如果用 SQLite,务必给
chunk_hash字段建UNIQUE索引,且关闭journal_mode=MEMORY提升写入速度(前提是可容忍崩溃丢少量数据)
校验失败时怎么定位是哪一块出问题
上传完成后,客户端和服务端各自生成“块哈希列表”的 Merkle 根(比如两两 SHA256 拼接再哈希),对比根哈希不一致,说明至少有一块不同。但用户需要知道具体哪几块坏了,才能重传,而不是整个重来。
实现上,服务端收到完整上传后,应返回一个校验响应,包含: {"status": "mismatch", "mismatch_blocks": [2, 7, 15]}。这要求服务端保存每块的原始哈希(不只是存在性),并支持按索引查值。
- 别在响应里直接返回所有块哈希——10 万块就是 3MB+ 流量,浪费带宽
- 客户端本地块哈希列表必须和上传顺序严格一致,否则索引错位,定位失效
- 网络传输中块顺序可能乱序?那就得在每块元数据里带上
block_index字段,服务端按 index 归并,而非依赖接收顺序
真正难的不是算哈希,而是让客户端和服务端对“同一块”的定义完全一致:块起始偏移、长度、填充规则、哈希算法版本——任何一处不一致,校验就变成玄学。


















