ios中accept="image/*"强制唤起相册,而".jpg,.png"可调文件app;android则碎片化,多数忽略mime仅匹配扩展名,且file.type不可靠,必须用文件名后缀+后端magic bytes双重校验。

移动端 accept 属性在 iOS 和 Android 上根本不是“同一套逻辑”——它不只影响显示哪些文件,更直接决定唤起哪个系统级选择器(相册 / 文件管理器 / 相机),且行为不可预测。别指望写一次就全平台生效。
accept="image/*" 在 iOS 和 Android 上触发的不是同一个界面
iOS Safari 下:accept="image/*" 会强制弹出「照片」App,用户看不到 iCloud、微信文件、下载目录;但若你写成 accept=".jpg,.png",它反而跳转到「文件」App,支持多来源选择。
Android 行为更碎片化:accept="image/*" 多数机型弹图库,但华为 EMUI、小米 MIUI 常忽略该属性,直接打开默认文件管理器;部分低端安卓 WebView 甚至完全不响应 accept,一律显示“所有文件”。
实操建议:
- 不要依赖
accept="image/*"实现“仅选图片”——它在 iOS 上太窄,在 Android 上又太松 - 明确业务目标:要调用相机?用
capture="camera"+accept="image/*";要从相册选?iOS 用accept="image/*",Android 建议留空或用accept=".jpg,.jpeg,.png" - 真机测试必须覆盖 iOS 15+、iOS 16+、Android 12~14 主流机型,模拟器无法复现 Intent 或 UIDocumentPicker 的真实行为
传 PDF/Excel 等文档时,accept 必须写扩展名,不能只写 MIME 类型
Android Chrome 对 accept="application/pdf" 基本无反应,iOS Safari 则可能直接禁用该 input(尤其 iOS 11 以下);但 accept=".pdf,.xlsx,.docx" 在双端都能稳定唤起「文件」App。
原因在于:移动端文件选择器基本不解析 MIME,只按扩展名字符串做简单匹配;而不同系统对同一文件上报的 file.type 差异极大(如 .jpg 在 Windows 是 image/pjpeg,macOS 是 image/jpeg)。
实操建议:
- 文档类上传统一用扩展名列表:
accept=".pdf,.xlsx,.xls,.docx,.doc,.csv" - 避免混用 MIME 与扩展名:
accept="application/pdf,.pdf"可能被某些安卓 WebView 解析失败 - 如果后端只认
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,前端仍得用.xlsx——这是妥协,不是错误
multiple + accept 组合下,用户仍可混选文件,且 file.type 不可靠
即使写了 accept=".jpg,.png" multiple,用户在 Android 文件管理器里仍能同时勾选 .jpg 和 .zip;iOS 在「文件」App 中也允许混选。更麻烦的是:file.type 在 iOS Safari 中常为空,Android 部分定制系统返回 text/plain 即使你选的是 PDF。
所以 beforeRead 阶段必须用 file.name 做正则校验,而不是依赖 file.type:
const ext = (file.name || '').match(/\.[^.]+$/i)?.[0]?.toLowerCase() || '';
实操建议:
- 校验逻辑必须放在
change或beforeRead回调中,不能只靠accept属性 - 扩展名正则要兼容大小写:
/\.(jpg|jpeg|png|pdf)$/i - 别信
file.size—— 某些安卓 WebView 在多选时对非首文件返回 0,需结合 Blob 构造验证
accept 属性不提供任何安全边界,后端必须重校验
用户长按文件 → “重命名” → 把 report.exe 改成 report.jpg,就能绕过所有前端限制;拖拽上传更完全无视 accept。浏览器只把它当 UI 提示,不是过滤闸门。
后端校验要点:
- 读取文件前 1024 字节,检查 magic bytes(如 PDF 开头是
%PDF,PNG 是‰PNG) - 比对实际 MIME 类型(
file.headers['content-type'])与扩展名是否匹配 - 拒绝
Content-Type: text/html却带.pdf后缀的文件——这是常见 XSS 注入手法
最易被忽略的一点:iOS Safari 14.5+ 使用 URL.createObjectURL() 预览大文件时存在内存泄漏,若用户反复上传预览,页面可能卡死;应优先用 FileReader.readAsArrayBuffer() + 后端转存临时链接方式处理。











