不能直接用md5.sum读整个文件,因大文件一次性加载会oom;应使用md5.new()+io.copy流式计算,自动32kb缓冲,内存可控,且需os.open+defer close,调h.sum(nil)获取结果。

为什么不能直接用 md5.Sum 读整个文件?
因为大文件(比如几个 GB)一次性加载进内存会触发 OOM,md5.Sum 的输入是 []byte,本质是内存拷贝。真正该用的是流式计算接口——hash.Hash 实现,配合 io.Copy 或分块读取。
正确做法:用 md5.New() + io.Copy 流式计算
这是最简洁、内存可控的方式,底层自动按 32KB 缓冲区读取,不依赖开发者手动分块。
- 打开文件时务必用
os.Open,不是os.ReadFile - 记得在 defer 中调用
f.Close(),否则句柄泄漏 - 计算完用
h.Sum(nil)获取结果,不是h.Sum([]byte{})(后者会多分配一次切片) - 如果需要十六进制字符串,用
fmt.Sprintf("%x", sum),别用hex.EncodeToString(它额外分配)
h := md5.New()
f, _ := os.Open("bigfile.zip")
defer f.Close()
io.Copy(h, f)
sum := h.Sum(nil)
fmt.Printf("%x\n", sum)
遇到 io: read/write on closed pipe 或 panic 怎么办?
常见于并发场景下多个 goroutine 共享同一个 hash.Hash 实例,hash.Hash 不是并发安全的。每次计算必须新建实例。
- 不要把
md5.New()提到循环外复用 - 不要在 goroutine 里传入同一个
h句柄 - 如果要批量校验多个文件,每个文件都应独立调用
md5.New() - 若需高性能并发,可考虑预分配
sync.Pool[*md5.digest],但普通场景没必要
要不要自己分块读取?io.Copy 和手动 bufio.Reader 有啥区别?
绝大多数情况不用手动分块。io.Copy 内部已优化:默认用 32KB 缓冲区,且对支持 ReadFrom 的目标(如 hash.Hash)会尝试零拷贝路径。手动分块反而容易出错。
- 除非你要在读取过程中做进度上报,才需要
io.ReadFull或bufio.Reader.Read - 手动分块时,最后一块长度可能不足缓冲区大小,别忘了用
n > 0判断,而不是只看err == io.EOF -
bufio.Reader对哈希计算无收益,还多一层内存拷贝,纯属画蛇添足











