accept属性仅优化前端文件选择体验,非安全机制;必须MIME+扩展名双写且逗号无空格;JS需校验file.name和magic bytes;后端须以文件头为唯一可信依据。

accept 属性只影响文件选择框,不是安全过滤器
它不会阻止用户上传非法文件,只是让浏览器在弹出对话框时默认隐藏不匹配的类型。用户点“所有文件”、拖拽、或删掉 accept 属性,照样能选 .exe、.html 甚至 .php。iOS Safari 可能因此禁用相册入口;华为 EMUI、小米 MIUI 的 WebView 常直接忽略该属性——这不是 bug,是设计如此。
accept 值必须 MIME + 扩展名双写,逗号后不能有空格
单写 application/pdf 或单写 .pdf 都容易失效:Safari(尤其 iOS)依赖 MIME 类型,安卓 WebView 对扩展名支持弱,Windows 可能上报 application/x-pdf 而非标准值。
-
accept="application/pdf,.pdf"✅ 同时覆盖 MIME 和扩展名,iOS/Safari/Edge/Chrome 兼容性最稳 -
accept="application/vnd.openxmlformats-officedocument.wordprocessingml.document,.docx"✅ .docx 的唯一可靠组合 -
accept="image/png,image/jpeg,.png,.jpg,.jpeg"✅ 显式列出变体,避免image/pjpeg被漏 -
accept="text/plain,.csv"✅ 逗号后无空格,旧版 Safari 才不会丢弃整个属性 -
accept="application/pdf , .pdf"❌ 逗号后有空格 → Safari 解析失败
前端 JS 必须遍历 files 列表做二次校验,别信 file.type
file.type 是浏览器推测值,可为空、可伪造:把 malware.exe 改名成 report.jpg,file.type 就可能变成 image/jpeg,但内容仍是可执行文件。
- 优先用
file.name.toLowerCase().endsWith('.pdf')或正则file.name.match(/\.(pdf|docx)$/i) - 关键业务加 magic bytes 校验:用
FileReader读前 4 字节,PDF 是%PDF(25 50 44 46),XLSX 是PK\x03\x04 - 设了
multiple后,event.target.files里可能混着合法与非法文件,必须遍历每一项,不能只检查第一个 - 提示要具体:“已选 2 个不支持的文件(
scan.tif,data.zip)”,而不是笼统说“类型错误”
后端才是唯一安全边界,且必须校验文件头而非扩展名
前端所有限制都可被绕过:curl、Postman、DevTools 修改 DOM、拖拽 Drop 区域……服务端收到的文件,Content-Type 请求头可伪造,$_FILES['file']['type'](PHP)、req.file.mimetype(Node.js)都不可信。
立即学习“前端免费学习笔记(深入)”;
- 必须读取文件二进制流前几个字节(magic bytes):PDF 开头必须是
%PDF,PNG 是\x89PNG,XLSX 是PK\x03\x04 - 推荐库:Node.js 用
file-type,Python 用python-magic,Java 用Apache Tika - 验证扩展名是否与真实内容一致(防伪装),保存时剥离原始扩展名,用服务端生成的随机名 + 白名单后缀(如
a1b2c3.pdf) - 上传目录禁止执行权限,静态资源走 CDN 或独立域名



















