accept属性仅优化文件选择界面,无法阻止非法上传;必须mime类型与扩展名双写(如"application/pdf,.pdf")、逗号后无空格,并配合前端校验file.name及后端基于magic bytes的文件头校验。

accept 属性只能提示浏览器过滤文件选择框,不能真正阻止非法文件上传——后端必须校验文件头(magic bytes),否则任何前端限制都形同虚设。
accept 属性怎么写才靠谱
单写 MIME 类型或单写扩展名都容易失效,尤其在 iOS Safari、安卓 WebView 或 Windows 文件管理器下。必须 MIME + 扩展名双写,且逗号后不能有空格。
-
accept="application/pdf,.pdf"✅ 覆盖主流场景 -
accept="image/png,image/jpeg,.png,.jpg,.jpeg"✅ 避免image/pjpeg等非标类型被漏 -
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,.xlsx"✅ .xlsx 的唯一可靠组合 -
accept="video/mp4,video/webm,.mp4,.webm"✅ 视频上传兜底写法 -
accept="text/csv,.csv"✅ CSV 文件别只写text/plain,太宽泛
JS 校验 file.name 比 file.type 更可靠
file.type 是浏览器推测值,拖拽上传时经常为空,改后缀名(如 hack.exe → report.jpg)后它就变成 image/jpeg,完全不可信。
- 用
file.name.toLowerCase().endsWith('.pdf')判断扩展名 - 或正则:
/\.(pdf|docx|xlsx|png|jpg)$/i.test(file.name) - 多个文件时必须遍历
event.target.files每一项,不能只看[0] - 大小检查用
file.size > 5 * 1024 * 1024(单位是字节)
后端必须读文件头,不是扩展名也不是 Content-Type
所有前端检查都能被绕过:curl、Postman、DevTools 改 DOM、拖拽 Drop 区域……服务端收到的 req.file.mimetype(Node.js)、$_FILES['file']['type'](PHP)或 Content-Type 请求头全可伪造。
- PDF 必须以
%PDF(十六进制25 50 44 46)开头 - PNG 是
\x89PNG,XLSX 是PK\x03\x04 - Node.js 推荐用
file-type库(读前 12 字节即可) - PHP 用
finfo_file($finfo, $tmpFile),禁用$_FILES['file']['type'] - Java Spring 中禁用
MultipartFile.getContentType(),改用Files.probeContentType()或 Apache Tika
容易被忽略的两个硬性前提
这两个问题不解决,accept 和 JS 校验再完善也收不到文件。
-
<form></form>必须带enctype="multipart/form-data",否则后端req.files为空,但控制台不报错,极易误判为 JS 问题 - 如果用 Fetch 或 XMLHttpRequest 上传,
FormData.append('file', input.files[0])后不能手动设Content-Type头——浏览器会自动加 boundary,手动设置反而会破坏 multipart 结构
真实安全边界只有一条:服务端读取上传文件的二进制流前几个字节,比对魔数。其余全是体验优化。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











