结论:分片下载后校验码字符串不能拼接,必须流式计算完整文件的sha256.Sum256。因SHA256是密码学哈希,拼接各分片哈希字符串与原始文件哈希无关;唯一可靠方式是用io.Copy流式计算合并后文件的完整哈希,且需确保文件覆盖写入、正确关闭句柄。

直接说结论:分片下载后校验码字符串不能拼接,必须流式计算完整文件的 sha256.Sum256,否则校验无效。
为什么不能拼接各分片的哈希字符串
拼接分片哈希值(如把每个 part_001 的 sha256 字符串连成一行)完全没意义——它既不等于原始文件哈希,也不等于合并后文件哈希。SHA256 是密码学哈希,输入任意字节序列变化都会导致输出雪崩式改变,拼接字符串只是在哈希值上做字符串操作,和原始内容零相关。
- 常见错误:把
hex.EncodeToString(part0[:]) + hex.EncodeToString(part1[:])当作“组合校验码” - 后果:哪怕文件完全正确,这个拼接串也 100% 和服务端提供的
sha256不一致 - 更危险的是:它会掩盖真实损坏——比如某分片末尾少写 3 字节,拼接哈希串仍能生成,但毫无校验作用
必须用 io.Copy 流式重算完整哈希
唯一可靠方式是打开最终合并完成的文件,用 sha256.New() 配合 io.Copy 一次性流式计算,不加载全文进内存。
- 目标文件必须用
os.Create(dstPath)创建(覆盖模式),不能是os.OpenFile(..., os.O_APPEND),否则可能残留旧数据干扰哈希 - 代码结构应为:
hash := sha256.New(); io.Copy(hash, file); sum := hash.Sum(nil) - 不要用
os.ReadFile或io.ReadAll——GB 级文件会 OOM - 别省略
defer file.Close(),未关闭句柄会导致后续哈希读取不完整
分片阶段可做局部校验,但不能替代全局哈希
每个分片下载完,可以立刻算它的局部哈希(比如服务端提供了每片的 sha256),这有助于快速定位哪一片出错,但局部校验通过 ≠ 全局正确。
立即学习“go语言免费学习笔记(深入)”;
- 局部哈希只验证该段字节是否与预期一致,无法发现:分片写入偏移错位、
WriteAt起始位置偏移错误、最后一片end超出total-1导致静默截断 - 例如:总大小 10MB,最后一片本该是
9_999_999字节,却因整除余数算成10_000_000,导致WriteAt写入失败或被忽略,文件末尾缺失,但所有分片局部哈希都对 - 所以局部校验是加速排错手段,全局哈希才是交付前不可跳过的终审
校验失败时别留脏数据
哈希不匹配,说明文件已损坏,此时最安全做法是立即删除目标文件和所有临时分片(*.part),而不是尝试修复或重试某一片——因为错误根源可能是服务端响应错乱、本地磁盘 I/O 错误或并发写入竞争,单点重试大概率重复失败。
- 删完后从头开始下载,或依赖断点续传逻辑(前提是状态记录可靠且分片元数据完整)
- 尤其注意:不要仅比对
os.Stat().Size,大小一致但内容错位的情况非常普遍(如某段写入偏移+1,后面全错位) - 真正麻烦的不是下载慢,而是“看起来下完了、大小对了、但 ZIP 打不开、视频播到一半卡死”——这种问题必须靠全局哈希拦截


















