HTML5本身不提供秒传功能,秒传是业务层基于文件哈希与服务端预存状态协同实现的体验优化;需用FileReader读取ArrayBuffer计算哈希,再通过fetch向服务端预检,服务端须校验hash+size双条件,Web Workers可缓解大文件哈希卡顿。

HTML5 本身不提供“秒传”功能,所谓秒传是业务层基于文件哈希与服务端预存状态协同实现的体验优化,不是浏览器原生能力。
为什么 FileReader + ArrayBuffer 是计算文件哈希的前提
秒传依赖客户端快速得出文件唯一指纹(如 MD5、SHA1),而 HTML5 中只有通过读取文件原始字节才能计算。直接读 File 对象的 arrayBuffer() 最可靠,避免文本编码干扰:
const file = document.querySelector('input[type="file"]').files[0];
const reader = new FileReader();
reader.onload = () => {
const buffer = reader.result; // ArrayBuffer
const hash = sparkMD5.arrayBuffer(buffer); // 需引入 spark-md5 库
console.log('file hash:', hash);
};
reader.readAsArrayBuffer(file);
- 不能用
readAsText():换行符、BOM、编码转换会破坏哈希一致性 - 大文件慎用全量读取:可分块读取 + 流式哈希(如
spark-md5的concat()或hashBinary()) - 注意 Safari 对大于 500MB 文件的
readAsArrayBuffer()可能触发内存警告
fetch() 发起秒传预检时必须带完整哈希与文件元信息
客户端算出哈希后,需向服务端发起一次轻量请求,询问该哈希是否已存在。这个请求不是上传,而是“查表”:
fetch('/api/upload/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
hash: 'd41d8cd98f00b204e9800998ecf8427e',
filename: 'report.pdf',
size: 10485760
})
})
.then(res => res.json())
.then(data => {
if (data.exist) {
console.log('秒传成功,服务端已存在');
// 直接走后续业务逻辑(如插入数据库记录)
} else {
console.log('需正常上传');
// 调用 multipart/form-data 上传流程
}
});
- 服务端必须校验
hash+size双条件,防止哈希碰撞误判 - 不要在 URL 中拼接哈希(易被 CDN 缓存或日志泄露),统一走 POST body
- 若服务端返回 304 或自定义状态码(如
{ code: 208, exist: true }),前端需明确识别,不能只看 HTTP 状态码
Web Workers 能缓解主线程卡顿,但不解决哈希计算本质耗时
对 >100MB 的文件,主线程执行哈希计算会导致页面冻结。把 sparkMD5 或 crypto.subtle.digest() 移入 Worker 是合理选择:
立即学习“前端免费学习笔记(深入)”;
// main.js
const worker = new Worker('hash-worker.js');
worker.postMessage({ type: 'hash', file });
worker.onmessage = ({ data }) => {
if (data.hash) uploadWithHash(data.hash);
};
-
crypto.subtle.digest()在 Worker 中可用,但 IE/旧 Edge 不支持;sparkMD5兼容性更好 - Worker 无法直接访问
File对象,需用file.slice().arrayBuffer()分块传 ArrayBuffer - 别忘了在 Worker 中加
self.close(),避免长期驻留
真正决定“秒传是否成立”的,是服务端是否保存了该哈希对应的有效文件实体及访问权限。前端所有哈希计算和预检,只是为绕过冗余上传做的必要试探——一旦服务端校验逻辑有漏洞(比如没校验文件大小、没清理过期哈希),秒传就会变成“假秒传”,甚至引发资源错乱。










