accept属性必须同时写mime类型和扩展名,如“application/pdf,.pdf”,逗号后不可有空格;file.type不可靠,需结合扩展名和魔数校验;后端必须基于文件头字节而非content-type或扩展名做最终验证。

accept 属性必须同时写 MIME 和扩展名,不能只写一个
单写 accept="application/pdf" 或 accept=".pdf" 都不可靠:iOS Safari 常忽略纯 MIME,部分安卓 WebView 只认扩展名,Windows 系统可能上报 application/x-pdf 而不是标准值。真正兼容的写法是两者并存、逗号分隔且**逗号后无空格**:
-
accept="application/pdf,.pdf"✅ 覆盖主流平台 -
accept="application/pdf , .pdf"❌ Safari 会整个丢弃该属性 -
accept="pdf"或accept="application/msword"❌ 非法值,浏览器直接忽略
前端 JS 必须遍历并校验每个文件,不能只看 file.type
file.type 是浏览器推测值,常为空、可伪造(比如把 malware.exe 改名成 report.pdf,file.type 仍可能是 application/pdf)。可靠做法是组合判断:
- 先用
file.name.toLowerCase().endsWith('.pdf')快速过滤扩展名 - 再用
FileReader读前 4 字节,比对 PDF 魔数%PDF(十六进制25 50 44 46) - 启用了
multiple时,event.target.files是FileList,必须Array.from(event.target.files).forEach(...)遍历每一项 - 发现非法文件要明确提示,例如“已跳过 1 个不支持的文件:
data.xlsx”
后端必须基于文件头(magic bytes)识别真实类型
所有前端限制都可被绕过:curl 直传、DevTools 删除 accept、拖拽到 Drop 区域……服务端收到的 Content-Type 请求头、req.file.mimetype(Node.js)、$_FILES['file']['type'](PHP)全不可信。
- 必须读取上传文件二进制流前若干字节,用魔数判定真实类型(PDF 开头必须是
%PDF) - 推荐库:
file-type(Node.js)、python-magic(Python)、finfo_file()(PHP) - 保存时剥离原始文件名,重命名为随机字符串 + 安全后缀(如
abc123.pdf) - 上传目录禁用执行权限,静态资源走 CDN 或独立域名
表单本身别漏关键配置
即使 accept 和 JS 校验都写对了,表单结构出错也会导致文件根本传不到后端:
-
<form></form>必须带enctype="multipart/form-data",否则req.files为空 - 错误现象:
req.file is undefined,但控制台无报错,容易误判为前端问题 -
<input type="file">应配name属性,后端靠它定位字段 - 移动端 WebView 兼容性差,不要依赖
image/*类通配符逻辑来兜底 PDF
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











