md5.Sum()适用于小字符串(如URL、配置键),返回结构体,需用fmt.Sprintf("%x", sum)或hex.EncodeToString(sum[:])转十六进制;md5.New()用于大文件或流式数据,须配合io.Copy(),调用Sum(nil)获取[]byte结果,二者语义不同不可混用。

md5.Sum() 和 md5.New() 不是两个可互换的“MD5函数”,而是语义完全不同的工具。选错就得不到正确哈希值,不是写法问题,是设计意图冲突。
字符串 MD5 用 md5.Sum(),但别直接打印结构体
小文本(URL、配置键、日志行)直接走 md5.Sum([]byte(s)),它返回一个结构体,内部是固定大小的 [16]byte 数组。
- 常见错误:写
fmt.Println(md5.Sum([]byte("a"))),输出类似{[138 214 110]}—— 这不是乱码,是结构体默认格式化,你要的是十六进制字符串 - 正确取值:用
fmt.Sprintf("%x", sum)或hex.EncodeToString(sum[:]) - 别写
string(sum[:]):可能含不可见字节,且越界 panic 风险高 - 别在循环里反复调
md5.Sum()并幻想复用:每次都是新结构体,无状态可共享
文件 MD5 必须用 md5.New() + io.Copy()
大文件或任何不能一次性加载进内存的数据,必须用 md5.New() 返回的 hash.Hash 接口,再配 io.Copy() 流式处理。
- 错误做法:
data, _ := os.ReadFile("big.zip"); md5.Sum(data)—— 几百 MB 就 OOM - 标准流水线:
file, _ := os.Open(path); defer file.Close(); hasher := md5.New(); io.Copy(hasher, file); hashBytes := hasher.Sum(nil) - 别手动
Read/Write:自己开buf循环读容易漏掉最后一次n < len(buf)的剩余字节 - 别写
md5.New().Sum([]byte("x")):参数是拼接缓冲区,不是输入数据,“x”会白加进去
Sum(nil) 和 sum[:] 的类型差异必须分清
md5.Sum() 返回结构体,md5.New().Sum(nil) 返回 []byte;二者都需转十六进制才能当哈希串用,但取值方式不同。
-
sum := md5.Sum([]byte("hi"))→ 转字符串用fmt.Sprintf("%x", sum)或hex.EncodeToString(sum[:]) -
h := md5.New(); h.Write([]byte("hi")); b := h.Sum(nil)→ 转字符串用fmt.Sprintf("%x", b)或hex.EncodeToString(b) - 误把
h.Sum(nil)当作md5.Sum()类型传给fmt.Sprintf("%x", ...)是安全的,但反过来不行 - 空文件能正常算:
md5.Sum([]byte(""))对应d41d8cd98f00b204e9800998ecf8427e,无需分支判断
符号链接、权限、错误检查这些点最容易被跳过
MD5 计算本身几乎不会出错,但 os.Open 和 io.Copy 阶段失败极频繁,且错误类型五花八门。
立即学习“go语言免费学习笔记(深入)”;
-
permission denied、read xxx: is a directory、no such file or directory都来自系统调用,不是crypto/md5的问题 - 符号链接默认被跟随;若只想算链接文件本身(而非目标内容),得先
os.Lstat()判断类型 -
io.Copy的err必须显式检查:磁盘满、文件被删、网络中断都会在这里提前返回,不检查就等于默认成功 -
os.Open后务必defer file.Close(),否则 fd 泄露,Linux 下很快触发too many open files


















