浏览器文件上传需前端去重:先用name+size+lastmodified三元组判断,目录上传时用webkitrelativepath;内容重复需sha-256哈希(限5mb内);后端必须二次校验,不可依赖前端。

上传前用 FileList 遍历比对文件名和大小
浏览器原生不提供自动去重逻辑,得自己在 input[type="file"] 的 change 事件里拦截处理。关键不是只看文件名——同名但内容不同要保留,所以至少得结合 name + size + lastModified 三者判断。注意 lastModified 是毫秒时间戳,部分旧版 Safari 返回的是秒级,建议统一转成字符串再比对。
实操建议:
- 用
Array.from(e.target.files)转成数组,方便去重操作 - 用
Map存已见的name+size+lastModified拼接键,避免双重循环 - 不要用
file.name单独作 key,用户可能手动改名导致漏判 - 如果业务允许,可加个提示:“已跳过重复文件:xxx.pdf(大小 2.1MB,修改时间 2024-05-20)”
FileReader 读取 arrayBuffer 做精准哈希去重(适合小文件)
仅靠文件名和时间戳无法识别重命名后的真实重复(比如把 a.jpg 改成 b.jpg),这时得比内容。但直接读全量二进制做 MD5/SHA1 成本高,实际中建议限制单文件 ≤5MB 再启用该流程。
实操建议:
- 对每个
File实例调用reader.readAsArrayBuffer(file) - 用
crypto.subtle.digest('SHA-256', buffer)算哈希(注意兼容性:IE 不支持,需降级为 SparkMD5 等库) - 哈希结果是
ArrayBuffer,转成十六进制字符串再存入Set判断是否已存在 - 别在主线程同步计算哈希,否则卡 UI;用
Promise包一层,配合await顺序处理或Promise.allSettled并行
后端必须二次校验,前端去重只是体验优化
前端任何去重都可被绕过:用户改前端代码、用 curl 传、甚至禁用 JS 后重复点选。所以后端收到文件后,仍要按业务规则检查——比如同一用户 24 小时内是否已上传过相同哈希的文件。
常见错误现象:
- 前端去重了,后端没校验,结果数据库里还是存了两份物理相同的文件
- 后端只比文件名,导致用户上传
report_v1.pdf和report_v2.pdf(实际内容一样)被当成不同文件 - 上传接口没返回去重结果,前端无法反馈“您本次共上传 3 个文件,其中 1 个已存在”
建议后端返回结构类似:{"uploaded": 3, "skipped": 1, "duplicates": ["invoice.pdf"]}
注意 webkitdirectory 批量选目录时的特殊行为
当使用 input[type="file"][webkitdirectory] 选整个文件夹时,e.target.files 里的 File 对象路径信息(webkitRelativePath)可能含斜杠,但 name 只是末级文件名。这时候如果只拼 name+size,会导致 img/a.png 和 doc/a.png 被误判为重复。
实操建议:
- 开启目录上传时,优先用
file.webkitRelativePath || file.name构建唯一键 - 若需跨目录去重(比如不管路径,只要文件内容一样就跳过),仍得走哈希方案
- Chrome 和 Edge 支持
webkitRelativePath,Firefox 不支持,需 fallback 到仅用name+size+lastModified
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











