crypto.subtle.digest 返回的是 ArrayBuffer,不是十六进制字符串,这是新手第一个卡点:
async function sha256Hex(text) {
const buf = new TextEncoder().encode(text);
const digest = await crypto.subtle.digest('SHA-256', buf);
return [...new Uint8Array(digest)]
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
三个前提条件
- 安全上下文:crypto.subtle 只在 HTTPS 或 localhost 下可用。用 file:// 打开页面时它是 undefined——本地双击 HTML 调试时最容易踩这个坑。
- 异步:digest 返回 Promise,没法同步拿到结果。
- 支持的算法有限:SHA-1、SHA-256、SHA-384、SHA-512。没有 MD5——它不属于 Web Crypto 标准,也不该再用于安全场景。
大文件怎么办
Web Crypto 不支持流式摘要,必须把整个文件读进内存。几百 MB 的文件在移动端会直接崩掉。这种场景应该交给服务端算,或者用支持增量更新的实现(比如 WASM 版的哈希库)。
几十 MB 以内的文件用它完全没问题,读文件用 file.arrayBuffer() 即可:
const buf = await file.arrayBuffer();
const digest = await crypto.subtle.digest('SHA-256', buf);
HMAC 与真正的加密
需要带密钥的签名用 subtle.sign,算法传 { name: 'HMAC', hash: 'SHA-256' },密钥要先 importKey。注意:算哈希不等于加密,前端做的任何校验都能被绕过,它只能用来验证完整性,不能当权限控制。
实际用途
- 上传前算文件指纹,实现秒传或去重命名。
- 对比两个文件是否完全一致,比逐字节比对快。
- 给内容生成稳定的缓存键。
- 校验下载文件的完整性,和发布方给的校验值比对。
SHA-256 的输出是 64 个十六进制字符。如果你拿到 32 位,那是 MD5;40 位是 SHA-1。长度先对,再谈内容对不对。
哈希计算工具就是用这套原生 API 实现的,可以拿来验证上面这段代码。