accept属性仅为UI提示,不能替代校验;需前后端结合校验,优先用file.type.startsWith('image/'),为空时正则匹配后缀,高要求场景须读文件头(magic bytes)比对,并及时清空input.value防事件失效。

accept 属性只是过滤器,不是校验器
它不能阻止用户选中非目标文件。比如 accept="image/*" 会让 Chrome 默认只显示图片,但用户点一下“所有文件”就能选 fake.jpg.exe;iOS Safari 甚至可能直接忽略这个属性。浏览器不会报错、不拦截、不校验——它只是个 UI 提示。后端必须做真实校验,前端也得补上 JS 检查。
file.type 和 file.name 要配合用,不能只信一个
file.type 经常为空(尤其 Windows 双击选中或安卓 WebView),也不能靠它识别篡改后缀的文件(比如把 malware.exe 改成 photo.jpg);file.name 又容易被手动改写或含多点(如 report.v2.final.jpg)。所以得组合判断:
- 先看
file.type.startsWith('image/'),快且有效 - 若
file.type为空,再用正则提取后缀:file.name.toLowerCase().match(/\.[a-z0-9]+$/) - 匹配时用
/\.(jpg|jpeg|png|gif|webp)$/i,带i标志避免大小写漏判 - 别用
file.name.endsWith('.png')—— 它对icon.PNG或data.csv.txt完全失效
真要严格校验?读文件头(magic bytes)
当业务要求高(比如医疗影像上传、合同 PDF 强制校验),光靠后缀和 MIME 不够。PNG 文件头永远是 89 50 4E 47,JPEG 是 FF D8 FF,这些没法伪造。实操步骤:
- 在
change事件里用FileReader读前 4–8 字节:reader.readAsArrayBuffer(file.slice(0, 8)) - 转成
Uint8Array后比对字节序列,例如:bytes[0] === 0x89 && bytes[1] === 0x50 - 注意:Safari 对
slice()的支持稳定,但旧 Android WebView 可能返回空 ArrayBuffer,需 fallback 到 name + type
别忘了 file.size 和 input.value 清空
校验超大文件(比如 >5MB)必须在 change 事件里立刻检查 file.size,单位是字节,别用除法算 MB——浮点误差会导致临界值误判。更关键的是:校验失败后必须执行 event.target.value = '',否则用户重选同名文件时 change 事件根本不会触发。这个细节线上故障率极高,但文档里几乎不提。
立即学习“前端免费学习笔记(深入)”;



















