accept属性仅影响文件选择器过滤,无法阻止非法上传;真正有效的校验只有change事件中的JS(扩展名+magic bytes)和后端文件头检测。

accept 属性根本不能阻止非法文件上传
它只影响系统文件选择器的默认过滤项,用户点“所有文件”、拖拽上传、删掉 accept 属性、甚至用 curl 直传,都能绕过。前端看到的 event.target.files 列表里,.exe、.psd、.zip 照样存在——这不是 bug,是设计如此。
真正起作用的只有两处:change 事件里的 JS 校验(扩展名 + magic bytes),以及后端读取文件头(不是 Content-Type,不是扩展名)做最终判定。把安全寄托在 accept 上,等于锁了门却把钥匙挂在门外。
accept 值怎么写才在多数浏览器生效
单靠 MIME 类型或单靠扩展名都容易失效:Safari 依赖 MIME,旧版安卓 WebView 只认扩展名,Windows 上文件可能报 application/x-pdf 而非标准 application/pdf。
- 必须双写:MIME 类型在前,扩展名在后,例如
accept="application/pdf,.pdf" - 多个值用英文逗号分隔,**逗号后不能有空格**:
accept="image/png,image/jpeg,.jpg,.png"✅,accept="image/png, .jpg"❌(旧 Safari 会丢弃整个属性) - 扩展名必须带点、小写、无空格:
.xlsx✅,xlsx或.XLSX❌ - 避免通配符混搭:
accept="image/*,.pdf"会让image/*覆盖后续精确匹配,不推荐
为什么 file.type 经常为空,而仅校验扩展名也不行
file.type 是浏览器根据扩展名或注册表推测的字段,不可信。Windows 下双击选的文件、拖拽进来的文件,file.type 大概率是空字符串;把 hack.exe 改成 report.pdf,file.type 就可能变成 application/pdf。
扩展名本身也能被手动篡改,所以必须分层校验:
- 第一层:用
file.name.toLowerCase().endsWith('.pdf')或正则/\.(pdf|xlsx|docx)$/i做白名单过滤 - 关键业务加第二层:用
FileReader读前 4 字节比对 magic bytes,例如 PDF 必须以%PDF(十六进制25 50 44 46)开头,XLSX 必须是PK\x03\x04 - 用了
multiple就得遍历Array.from(event.target.files),不能只看第一个
移动端特别是 iOS Safari 的 accept 行为差异
iOS Safari 对 accept 支持极弱,很多写法直接失效:
-
accept="image/*"在某些版本会强制调起相机,而不是相册;想选图不拍照,得写死accept="image/jpeg,image/png" -
accept=".heic"几乎无效,HEIC 格式默认不被识别 -
accept="application/pdf"在 iOS 上可能完全不生效,必须补上.pdf -
capture="camera"时,accept会被忽略,实际拍摄格式由设备决定
这意味着:在 iOS 上,你写的 accept 值大概率只是个摆设,真正能拦住用户的只有 change 事件里的 JS 提示——而且得提示具体文件名,比如“scan.heic 不支持,请重选 JPG 或 PNG”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











