应使用流式计算避免OOM:用os.Open打开文件,sha256.New()创建哈希器,io.Copy写入,不加载全文件;超大文件可加进度回调或bufio按块读取。

用 sha256.Sum256 计算大文件哈希不卡死
直接读整个文件进内存算哈希,1GB 文件就可能 OOM 或卡住几秒——这不是秒传,是“等传”。必须流式计算。
实操建议:
- 用
os.Open()打开文件,得到*os.File,别用ioutil.ReadFile() - 创建
sha256.New()哈希器,然后用io.Copy(hasher, file)流式写入,不占额外内存 - 对超大文件(>5GB),可加进度回调或按块读取(
bufio.NewReaderSize(file, 4),但多数场景 <code>io.Copy足够
示例关键行:
h := sha256.New()<br>_, _ = io.Copy(h, file)<br>sum := h.Sum(nil) // 得到 [32]byte
服务端怎么存哈希才支持快速查重
只把 sha256 结果当字符串存 MySQL 的 VARCHAR(64),查一次要 10ms+,并发一高就拖慢上传判断。得让索引真正生效。
立即学习“go语言免费学习笔记(深入)”;
实操建议:
- 数据库字段用
BINARY(32)存原始[32]byte,不是 hex 字符串——体积小、比较快、索引效率高 - Go 里入库前用
hex.DecodeString(s)转回字节数组;查询时也传字节数组,别转字符串再 like 匹配 - 加唯一索引:
CREATE UNIQUE INDEX idx_hash ON files(hash);,避免重复插入
客户端上传前怎么安全比对哈希(防中间人篡改)
如果客户端算完哈希直接发给服务端问“有没有”,中间人截获后伪造一个已存在的哈希,就能跳过上传——实际传的是恶意文件。
实操建议:
- 服务端不提供“查哈希是否存在”这种裸接口。改成:客户端先发哈希,服务端返回
{exists: true, token: "xxx"}或{exists: false} - 若存在,客户端必须带着这个一次性
token发起后续上传(如分片上传的初始化请求),服务端校验 token 绑定的哈希和文件一致 - token 用 HMAC 签名生成,有效期短(比如 60 秒),且绑定客户端 IP + 哈希值,防止重放
为什么不能只用文件名或大小做预判
同名不同内容、同大小不同内容太常见——比如两个 1.2MB 的 PDF,内容完全不同。靠这个判断“秒传”,等于默认用户从不重命名、从不生成同尺寸文件。
实操建议:
- 文件名/大小只能作为第一层快速过滤(比如缓存中没命中就跳过),绝不能替代哈希比对
- 如果业务允许弱一致性(如日志归档),可用
xxhash替代sha256加速,但生产级文件去重必须用密码学哈希 - 注意:
sha1已不安全,md5更不行,必须用sha256或更强
哈希本身不耗 CPU,耗的是磁盘 IO 和网络传输——真正卡点永远在读文件和查库,不是算哈希那几微秒。


















