iOS Safari 中 accept="application/pdf, image/*" 无法选择 PDF 是因 Safari 对通配符与 MIME 混用解析有缺陷,需显式列出所有类型及扩展名;file.type 不可靠,应结合文件扩展名和 Magic Bytes(如 %PDF)校验;accept 和前端 JS 校验仅作辅助,后端必须通过文件头字节验证真实类型。

accept="application/pdf, image/*" 为什么在 iOS Safari 上选不出 PDF
这不是你写错了,是 Safari(尤其 iOS)对 image/* 和 application/pdf 的组合解析有缺陷:它会优先匹配通配符规则,而忽略后续的 MIME 类型。结果就是 PDF 文件被过滤掉,用户根本看不到。
必须拆开写、显式列出所有目标类型,并补上扩展名:
-
accept="application/pdf,.pdf,image/png,image/jpeg,image/webp,.png,.jpg,.jpeg,.webp"✅ - 单写
image/*或application/pdf都不可靠,Safari 不认通配符 + MIME 混用 - 逗号后绝对不能有空格,
application/pdf , .pdf会被旧版 Safari 完全丢弃该属性 - Android WebView 常忽略
application/pdf,只认.pdf;macOS 则可能反过来看重 MIME 而弱化扩展名
为什么 file.type === 'application/pdf' 会是 false,即使文件真是 PDF
file.type 是浏览器根据文件头或扩展名推测的,不是真实内容判定。重命名 report.exe 为 report.pdf 后,file.type 可能为空字符串,也可能变成 application/octet-stream,甚至某些系统上报 application/x-pdf —— 这些都不等于 application/pdf。
更稳的做法是结合扩展名和字节头:
立即学习“前端免费学习笔记(深入)”;
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 先用
file.name.toLowerCase().endsWith('.pdf')快速过滤 - 再用
FileReader读前 4 字节,比对%PDF(十六进制25 50 44 46) - 图片同理:
\x89PNG(PNG)、\xFF\xD8\xFF(JPEG) - 别只校验第一个文件,
event.target.files是FileList,得遍历每一项
multiple 上传时 accept 不会自动剔除非法文件
写了 accept=".pdf,.png" 并启用 multiple,用户仍可混选 script.js、data.zip 和 report.pdf —— 浏览器不会帮你过滤掉非法项,event.target.files 里三项全在。
前端 JS 必须手动筛:
- 遍历
Array.from(event.target.files) - 对每个
file做扩展名 + magic bytes 双校验 - 发现非法文件时,明确提示具体文件名,比如“已跳过 1 个不支持的文件:
config.json” - 清空 input 的值用
e.target.value = '',而不是隐藏 DOM 元素
后端校验 PDF 和图片的真实类型不能只看扩展名
前端所有限制都可被绕过:curl 直传、DevTools 删除 accept、拖拽到 Drop 区域……后端收到的 Content-Type 请求头、req.file.mimetype(Express)、$_FILES['f']['type'](PHP)全是客户端可控字段,完全不可信。
唯一可信的是文件开头几个字节:
- Pdf:必须以
%PDF开头(25 50 44 46) - PNG:必须是
\x89PNG(89 50 4E 47) - JPEG:必须是
\xFF\xD8\xFF(FF D8 FF) - 推荐 Node.js 用
file-type库,Python 用python-magic,Java 用Apache Tika - 别用
path.extname()或originalname做判断,那是最危险的防线
真正容易被忽略的点是:前端 accept 和 JS 校验只是减少无效请求,后端不做 magic bytes 检查,就等于没设防。


















