浏览器对file input重复选同名文件不触发change事件是规范行为,需用SHA-256哈希内容去重、手动清空input.value重置状态,并强制服务端二次校验。

为什么input.files重复选同名文件不触发change事件
浏览器对input[type="file"]做了特殊处理:如果用户第二次选择**完全相同的文件路径(在本地文件系统中)**,哪怕只是点开对话框再点取消、或重新选一遍,change事件都不会再次触发。这是因为input.files对象未发生变更,浏览器认为“没新东西”。这不是 bug,是规范行为。所以仅靠监听change + 检查files.length,会漏掉重复添加同一文件的场景。
用FileReader读取内容哈希做真正去重
仅比对file.name和file.size不可靠——两个不同内容的文件可能同名同大小;反之,同一文件重命名后就逃过检测。唯一可靠方式是基于二进制内容生成指纹:
-
crypto.subtle.digest('SHA-256', await file.arrayBuffer())是当前标准做法,但要求页面运行在 HTTPS 下(HTTP 环境会抛SecurityError) - 大文件(如 >10MB)慎用全量哈希,可改用分块采样:取前 64KB + 后 64KB +
file.size拼接后哈希,精度损失小、性能提升明显 - 哈希结果转为十六进制字符串后,存入一个
Set或Map,每次新增文件前先查重 - 示例关键逻辑:
const getFileHash = async (file) => { const buffer = await file.arrayBuffer(); const hash = await crypto.subtle.digest('SHA-256', buffer); return Array.from(new Uint8Array(hash)) .map(b => b.toString(16).padStart(2, '0')) .join(''); };
必须手动清空input.value才能重置状态
即使你已用哈希去重并收集了有效文件,如果不主动重置输入框,用户下次打开选择框时仍可能“看不见”刚选过的文件——因为input.files还挂着旧引用,且change不会触发。正确做法是:
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 每次处理完
change事件后,立即执行input.value = '' - 不要依赖
input.files.length === 0判断是否“空”,而应以你维护的文件哈希集合为准 - 若用了
multiple,需遍历event.target.files每一项单独计算哈希、去重、加入集合,不能只看总数 - Safari 16.4+ 才完整支持
crypto.subtle.digest,IE 完全不支持;降级方案只能退到 name+size+lastModified 组合判断(明确告知用户“精度有限”)
服务端二次校验不是可选项,而是上线前提
前端哈希去重纯属体验优化。攻击者可绕过 JS 直接发请求,或伪造哈希值。生产环境必须:
立即学习“前端免费学习笔记(深入)”;
- 上传时把前端算出的哈希值作为字段(如
file_hash)随文件一起提交 - 服务端收到后,独立计算该文件内容的 SHA-256,与传来的值比对;不一致则拒绝
- 数据库中按哈希值建唯一索引,防止同内容文件入库多次
- 禁止仅靠文件名或数据库主键判断是否重复——恶意用户可上传
invoice.pdf和invoice (1).pdf,内容却完全一样
真正难的不是前端哈希,而是服务端如何高效处理大文件哈希而不阻塞请求;这部分逻辑一旦写错,前端所有努力都白费。


















