现代浏览器可用FileReader与crypto.subtle.digest()前端计算文件SHA-256哈希,需将File转ArrayBuffer后调用digest(),结果转十六进制字符串;大文件应分块处理或使用hash-wasm等库;哈希比对需统一编码格式,其核心用途是去重与一致性校验,而非防篡改。

HTML5 用 FileReader + crypto.subtle.digest() 计算文件 SHA-256 哈希
现代浏览器(Chrome 80+、Firefox 74+、Edge 79+)原生支持在前端计算文件哈希,无需上传到服务端即可完成校验。核心是组合使用 FileReader 读取二进制数据,再用 crypto.subtle.digest() 计算 SHA-256(或 SHA-1/SHA-512)。注意:digest() 只接受 ArrayBuffer,不能直接传 Blob 或 File。
实操要点:
-
File对象需先调用.arrayBuffer()(推荐)或用FileReader.readAsArrayBuffer()获取原始字节 - 必须在 HTTPS 环境或
localhost下运行,否则crypto.subtle会抛SecurityError - 大文件(>500MB)可能触发内存压力,建议分块读取(见下节)
- SHA-256 是当前推荐,默认输出为 32 字节
Uint8Array,需转为十六进制字符串才便于比对
async function calculateSHA256(file) {
const buffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
// 使用示例
document.querySelector('#file-input').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (file) {
const hash = await calculateSHA256(file);
console.log('SHA-256:', hash); // 如:a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
}
});
大文件哈希要避免 file.arrayBuffer() 一次性加载
直接调用 file.arrayBuffer() 会将整个文件载入内存,1GB 文件就占 1GB RAM,极易卡死或被浏览器终止。真实场景中应分块读取 + 流式哈希(类似 Node.js 的 crypto.createHash)。但浏览器 Web Crypto API 不支持增量 digest,所以需手动拼接分块哈希 —— 这**不等价于整体哈希**,不可用于校验。
正确做法是:用 File.slice() 分段读取,全部加载进内存后一次性调用 digest();或改用第三方库如 spark-md5(仅支持 MD5)或 hash-wasm(支持 WASM 加速的 SHA-256,可流式)。
立即学习“前端免费学习笔记(深入)”;
关键提醒:
- 不要自己实现“分块异或哈希”或“拼接哈希字符串”,结果与标准 SHA-256 完全不同
-
hash-wasm的sha256Multi()支持传入多个ArrayBuffer,内部按标准方式处理,等效于整文件哈希 - 若必须纯原生、无依赖,且文件确定 ≤200MB,仍可用
file.arrayBuffer(),加 loading 提示和 try/catch 防崩溃
哈希值比对失败常见原因:编码、截断、大小写
前端算出的哈希和后端/预存值比对不上?大概率不是算法问题,而是字符串表示不一致。SHA-256 原始结果是 32 字节二进制,转换成字符串时有三种常见变体:
-
hex(小写十六进制,64 字符):最常用,hash-wasm和多数服务端默认输出 -
hex(大写):Pythonhashlib.sha256().hexdigest().upper()会这样,需统一转小写再比对 -
base64:Node.jscreateHash('sha256').update(buf).digest('base64')输出 44 字符,含+//,前端需用atob()解码后再 digest —— 但更稳妥的是服务端也返回 hex
另外注意:
- 某些旧系统截断哈希(如只取前 16 字节),导致长度只有 32 字符,这不是标准 SHA-256
- 文件内容差异:前端读的是用户选中的
File,而后端收到的可能是经 multipart 解析、重命名、加时间戳后的文件,字节流已不同 - 换行符归一化:文本文件若在 Windows 上传(CRLF),服务端保存为 LF,哈希必然不同 —— 校验前需确认是否做了 normalize
文件哈希检测的真实用途不是防篡改,而是去重与一致性确认
前端计算哈希**不能替代 HTTPS 传输加密或服务端校验**。它无法防止中间人替换文件内容后再伪造哈希(因哈希本身也走同一通道),也不能防御恶意用户修改本地 JS 绕过计算逻辑。它的实际价值集中在:
- 上传前快速判断是否已存在相同文件(服务端查哈希索引),避免重复存储
- 断点续传时验证已上传分片的完整性(配合分片哈希列表)
- 用户侧确认“我选的这个文件”和服务端最终保存的“那个文件”字节一致(比如医疗影像上传后回显哈希供人工核对)
- 离线场景下,PWA 缓存资源更新时用哈希识别变更
真正需要防篡改的场景(如固件升级包),必须由可信源签名(RSA/ECDSA),前端验证签名,而非只验哈希。











