前端生成文件唯一指纹必须基于内容,使用crypto.subtle.digest()计算sha-256;超大文件需分块读取并增量哈希,严禁依赖file.name或lastmodified,避免oom和哈希不一致。

前端生成文件唯一指纹必须基于内容,不能依赖 file.name 或 file.lastModified——重命名、改时间、复制粘贴都会让这些字段失效,但内容不变,指纹就必须一致。
用 crypto.subtle.digest() 计算 SHA-256(推荐)
现代浏览器原生支持流式哈希计算,不卡主线程,且结果强一致:
-
crypto.subtle.digest()只接受ArrayBuffer,不能直接传File对象;需先用file.arrayBuffer()读取(注意:大文件会内存溢出) - 超大文件(>500MB)必须分块读取 + 增量 digest:用
file.slice(start, end)每次取 2–4MB,调用hasher.update()(需用js-sha256等支持增量的库,原生crypto.subtle不支持) - 切片时务必用字节偏移量,比如
file.slice(0, 4 * 1024 * 1024),别写'4MB'或4194304这种魔法数字——可读性差还易错 - 顺序不能乱:必须按原始字节流从头到尾分块,跳块或乱序会导致哈希值完全不同
为什么不用 MD5 或客户端 FileReader.readAsDataURL()
MD5 碰撞风险高,且浏览器里用 FileReader 读完整文件再转 Base64 再哈希,等于把整个文件加载进内存+字符串化,2GB 文件直接触发 OOM;readAsDataURL() 还额外引入 Base64 编码开销,哈希对象变成编码后字符串,和真实字节哈希不等价。
- 服务端校验时若用原始字节算 SHA-256,而前端用 Base64 字符串算,必然不匹配
- 部分旧安卓 WebView 不支持
file.arrayBuffer(),需降级为FileReader.readAsArrayBuffer()+ 分块轮询 - HTTP 需 HTTPS 环境才能启用
crypto.subtle,本地file://协议下会报SecurityError
秒传预检前必须做两件事
拿到哈希值只是第一步,真正落地要防三个坑:
- 上传前清空
input[type="file"].value:否则用户重复选同个文件,change事件不触发,后续逻辑断掉 - 把哈希值作为
uploadId发起预检请求,如/api/upload/check?hash=abcd123...,服务端返回已存fileId或缺失 chunk 列表 - 不要在 localStorage 存完整哈希值用于“去重提示”——用户换设备、清缓存就失效;它只该用于断点续传上下文,不是长期身份凭证
最常被忽略的是:分块计算时没做 Math.min(end, file.size) 边界兜底,最后一块越界导致 slice() 返回空 Blob,哈希值错误;还有人用 TextEncoder 把 ArrayBuffer 当字符串 encode,彻底破坏二进制一致性。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











