javascript前端通过文件名、大小、最后修改时间生成三元组指纹,结合map缓存60秒内上传记录实现轻量防重复;大文件不推荐主线程读取内容哈希,服务端需用redis二次校验并设ttl兜底。

用 JavaScript 缓存文件指纹防重复上传
浏览器本身不提供“禁止同一文件一分钟内重传”的原生机制,必须靠前端主动识别并拦截。核心思路是:对每个选中的 File 对象生成唯一指纹(如基于文件名 + 大小 + 最后修改时间),再结合时间戳做本地缓存校验。
常见错误现象:
用户连续点击两次上传按钮,后端收到两份完全相同的文件;或用户改名后重选同一文件,前端无法识别为重复。
-
File.name单独不可靠——用户可随意改名 -
File.lastModified在部分系统(如 macOS 保存副本)可能被重置,需配合大小校验 - 仅用
setTimeout清空变量容易漏掉并发操作,应使用 Map 或 WeakMap 存储带过期时间的记录
实操建议:
在 input[type="file"] 的 change 事件中,立即计算指纹并检查是否在 60 秒内出现过:
const uploadCache = new Map(); // key: fingerprint, value: timestamp
<p>function getFileFingerprint(file) {
return <code>${file.name}-${file.size}-${file.lastModified}</code>;
}</p><p>fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;</p><p>const fp = getFileFingerprint(file);
const now = Date.now();</p><p>if (uploadCache.has(fp) && now - uploadCache.get(fp) </p><p>uploadCache.set(fp, now);
// 继续执行上传逻辑...
});
</p>
为什么不用 FileReader 读取内容哈希?
理论上用 FileReader 计算 SHA-256 能真正识别内容重复,但实际项目中几乎不这么做——大文件(如 500MB 视频)读取+哈希会卡住主线程、消耗内存,且用户感知明显延迟。
性能与体验权衡点:
• 小于 10MB 的文件可考虑加一层内容哈希(需用 Web Worker 避免阻塞)
• 大多数业务场景下,“名字+大小+修改时间”三元组已覆盖 95% 以上重复场景
• 若服务端有去重能力(如对象存储 ETag 校验),前端指纹只需辅助,不需强一致
注意:file.lastModified 是毫秒时间戳,但部分浏览器(如旧版 Safari)可能精度只有秒级,若要求严格,可降级为只比对 name 和 size,并提示“可能重复”而非直接拦截。
服务端兜底时怎么传指纹给后端?
前端生成的指纹不应作为信任依据,必须同步传给后端用于二次校验。否则绕过 JS 就能重复提交。
- 把指纹作为额外字段加入
FormData:formData.append('fingerprint', fp) - 后端收到后,查数据库或 Redis 中该指纹最近 60 秒内的上传记录
- Redis 键建议设为
upload:fingerprint:{fp},TTL 设为 60 秒,写入即自动过期 - 不要依赖前端传来的
timestamp,后端一律用new Date()记录时间
容易被忽略的一点:如果用户用不同浏览器或无痕窗口上传同一文件,前端指纹无法共享,此时只能靠服务端内容哈希或分片比对来识别——但这属于另一层设计,和本问题的“前端轻量防重复”目标不在同一层级。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











