必须用 FileReader.readAsArrayBuffer() 将 File 转为 ArrayBuffer 后传入 crypto.subtle.digest();不可用 TextEncoder 处理文件对象;校验须用 timingSafeEqual() 比对 Uint8Array,且环境必须为 HTTPS。

不能直接用 crypto.subtle.digest() 处理文件对象——它只接受 ArrayBuffer 或 TypedArray,而 File 是 Blob 子类,必须先读取为字节流。
File 对象必须用 FileReader 或 Stream 转成 ArrayBuffer
浏览器里没有“同步读文件到内存”的安全接口,FileReader.readAsArrayBuffer() 是最兼容、最可控的方式。别用 readAsText() 再转编码,会破坏二进制一致性;也别试图用 URL.createObjectURL() 绕过——那只是生成临时 URL,不解决哈希计算问题。
-
FileReader在读取完成时触发load事件,reader.result才是可用的ArrayBuffer - 大文件(>100MB)慎用此方式,可能触发内存压力;若需流式处理,得用
File.stream()+ReadableStream.getReader()分块喂给sha256,但 Web Crypto 不支持增量 digest,所以仍需全量加载 - 注意
File实例可能来自<input type="file">或拖拽,确保你拿到的是真实用户选择的文件,而非伪造的FileList[0]空引用
digest() 输入必须是 UTF-8 字节流,但文件本身就是二进制
对文件做 SHA-256,不需要 TextEncoder。那是为字符串设计的。文件内容是原始字节,FileReader 返回的 ArrayBuffer 就是你要的输入。误用 TextEncoder.encode(file.name) 或其他字符串操作,算出来的根本不是文件哈希。
加密货币综合分析工具,集成OKX、Binance数据,提供行情、技术分析、交易信号、资金流向、仓位管理、DCA计划、风险计算。触发词:分析BTC、行情查询、交易信号、仓位计算、DCA计划、巨鲸追踪、风险评估。
- 正确做法:
await crypto.subtle.digest('SHA-256', reader.result) - 错误写法:
const enc = new TextEncoder(); await crypto.subtle.digest('SHA-256', enc.encode(file))——file不是字符串,enc.encode()会调用toString()得到[object File] - 如果服务端返回的是 base64 编码的参考哈希(比如 API 响应里带
"hash": "a1b2c3..."}),记得用atob()解码后再比对;若返回十六进制字符串,则要转成Uint8Array才能用于timingSafeEqual()
校验必须用 timingSafeEqual(),且比对目标是 Uint8Array
=== 或 == 比字符串会暴露哈希长度和前缀,构成计时侧信道。但 timingSafeEqual() 只接受两个 ArrayBuffer 或 TypedArray,不能直接传字符串。
- 把服务端给的 hex 字符串转成
Uint8Array:用Uint8Array.from(match.match(/.{2}/g).map(b => parseInt(b, 16))) - 把本地算出的
ArrayBuffer包装成Uint8Array:new Uint8Array(hashBuffer) - 调用:
await crypto.subtle.timingSafeEqual(localHash, remoteHash)—— 注意两个参数类型必须一致,且都是Uint8Array或都为ArrayBuffer - 如果校验失败,建议打印两者的十六进制表示(
Array.from(hash).map(b => b.toString(16).padStart(2,'0')).join('')),方便定位是传输截断、编码错位还是根本就不是同一份文件
非 HTTPS 环境下 crypto.subtle 会不可用
开发阶段用 http://localhost 没问题,但一旦部署到测试或预发环境,只要协议是 http://(哪怕域名是 test.example.com),crypto.subtle 就是 undefined,调用 digest() 会直接抛 SecurityError。
- 上线前务必确认部署环境是 HTTPS;HTTP/2 或 HTTP/3 不等于安全上下文,关键看协议头是不是
https:// - 不要在
try/catch里静默吞掉SecurityError,应明确提示“当前环境不支持完整性校验,请使用 HTTPS 访问” - 某些企业内网用自签名证书或代理重写,也可能导致
crypto.subtle不可用,这类情况只能靠运行时检测 + fallback 提示
真正容易被忽略的点是:校验逻辑本身没 bug,但文件读取中途被用户取消、FileReader 出错未捕获、hex 字符串大小写混用、或服务端哈希多包了一层 JSON 结构(比如返回 {"hash":"..."} 却忘了 JSON.parse()),这些都会让哈希比对失败——而它们和密码学无关,纯属工程衔接细节。

















