应使用 MD5.Create() 获取实例,统一用 Encoding.UTF8 编码字符串,流式处理大文件,用 BitConverter.ToString(hash).Replace("-", "").ToLower() 转哈希字符串,校验优先比对字节数组。
用 MD5.Create() 而不是 new MD5(),否则会报错
因为 md5 是抽象类,直接 new md5() 编译不通过。.net 中所有哈希算法都这样设计,必须用工厂方法获取实例。
-
MD5.Create()返回的是具体实现(如MD5CryptoServiceProvider),线程安全、可重用 - 旧写法
new MD5CryptoServiceProvider()虽然能用,但已标记为 obsolete,.NET 6+ 会警告 - 别在循环里反复调用
MD5.Create()——它内部有缓存机制,单例复用更高效
字符串转 MD5 前必须指定编码,Encoding.UTF8 是默认且最常用选择
MD5 计算的是字节流的哈希,不是字符串本身。同一个字符串用 UTF8 和 Unicode 编码后字节完全不同,结果必然不同。
- 错误示范:
md5.ComputeHash(Encoding.Default.GetBytes(str))——Encoding.Default依赖系统区域设置,跨机器结果不可控 - 正确做法:统一用
Encoding.UTF8.GetBytes(str),尤其处理 JSON、HTTP 请求体等场景 - 如果明确要兼容旧系统(如某些遗留数据库用 GB2312 存文本),才改用对应编码,但必须写死、不能靠环境
计算大文件 MD5 时别一次性读进内存,用 Stream 流式处理
对几百 MB 以上的文件调用 File.ReadAllBytes() 容易触发 OutOfMemoryException,而且毫无必要——MD5 算法天然支持分块输入。
- 正确方式:
using var fs = File.OpenRead(path); md5.ComputeHash(fs); - 如果还要同时读取文件内容做其他处理,可用
CryptoStream或手动分段调用TransformBlock()+TransformFinalBlock() - 注意:
ComputeHash(Stream)会把流位置移到末尾,后续再读需先stream.Position = 0
MD5 输出是字节数组,转十六进制字符串时别用 ToString("X2") 直接调用
byte[] 没有重载 ToString(string),那样只会得到类型名,不是哈希值。
- 正确转换:
BitConverter.ToString(hash).Replace("-", "").ToLower(),或用string.Create避免中间字符串分配(.NET 5+) - 别用
hash.Select(b => b.ToString("x2")).Aggregate((a,b) => a+b)——LINQ 分配多、性能差,小文件看不出来,高并发服务会明显拖慢 - 如果用于校验(比如比对上传文件),建议直接比对
byte[],避免字符串转换引入额外开销和编码歧义
MD5 本身已不适用于安全敏感场景(碰撞太容易),但做本地文件完整性校验、缓存键生成这类非密码学用途,只要保持编码和流程一致,结果就是稳定可靠的。真正容易出问题的,往往不是算法,而是字节来源没理清、流没关好、大小写混用导致比对失败。











