H5秒传需在choose回调中用SparkMD5分块计算MD5并挂载到实例,before钩子异步校验服务端存在性,服务端需独立哈希校验;大文件建议采样哈希,注意HTTPS及浏览器兼容性。

在 H5 中实现上传前 MD5 校验以支持秒传识别,核心是「在文件真正发出请求前,算出其内容指纹,并和服务端已存记录比对」。这一步必须卡死在用户选中文件后、HTTP 请求发起前,否则就失去了“秒传”的意义。
choose 回调是唯一可行的校验入口
Layui 或原生 input[type="file"] 的 change 事件之后,只有 choose 回调(Layui)或 FileReader 启动时机(原生) 能访问到原始 File 对象。before 上传拦截钩子拿不到 file,done/error 已完成通信——错过 choose,就再没机会读二进制内容了。
- 必须用
file.slice()分块读取(推荐 2MB/块),避免大文件直接readAsArrayBuffer()导致内存溢出或页面卡死 - 使用
SparkMD5.ArrayBuffer增量计算:每读一块就spark.append(arrayBuffer),全部读完再调一次spark.end()获取最终哈希值 - 计算完成后,把 MD5 字符串手动挂到上传实例上,例如
obj.md5 = md5Value,供后续逻辑使用
before 回调中发起服务端存在性校验
MD5 算完不能直接上传,需先确认服务端是否已有该文件。这个判断必须放在 before 钩子里,且要返回 Promise:
- 同步返回
false(Layui ≥ 2.6.8)可拦截,但仅适用于前端黑名单等简单场景 - 真实业务必须异步查服务端,例如:
return new Promise((resolve, reject) => { $.get('/api/check-md5', {md5: obj.md5}, res => res.exists ? reject('已存在,跳过上传') : resolve()); }) - 注意:不能写成
return $.get().then(...),那样 resolve 的是响应体,不是拦截动作
服务端配合完成秒传判定
前端只负责“报信”,秒传是否生效取决于后端逻辑:
- 接口
/api/check-md5收到 MD5 后,查询数据库或缓存中是否已存在完整文件记录 - 若存在,直接返回成功标识 + 文件访问路径,前端跳过上传,直接走“上传成功”流程
- 若不存在,但有该文件的部分分片(用于断点续传),可返回已传分片列表,前端上传时跳过对应块
- 切记:服务端仍需在接收文件时独立计算哈希并校验,前端校验只是体验优化,不可替代服务端落地校验
大文件与兼容性注意事项
不是所有环境都适合全量计算 MD5:
- HTTPS 是前提:若用
crypto.subtle.digest('SHA-256'),非 HTTPS 下会报错;SparkMD5 无此限制,更稳妥 - Safari 16.4+ 才完整支持
crypto.subtle,IE 完全不支持;SparkMD5 兼容性更好 - 超大视频/压缩包建议采样哈希:取前 64KB + 后 64KB + 总大小拼接后计算,兼顾性能与去重准确率
- 上传完成后记得清空
input.value = '',否则重复选同一文件不会触发 change,导致校验逻辑丢失


















