accept="image/*" 不够用,因其仅为浏览器提示而非强制约束,iOS Safari等会忽略,需配合JavaScript校验MIME类型和文件扩展名,并后端重验文件头、大小及重命名。

accept="image/*" 是最直接有效的设置方式,但仅靠它不能完全保证用户只选图片——iOS Safari、部分安卓浏览器会忽略该属性,必须配合 JavaScript 校验。
为什么 accept="image/*" 不够用
这个属性只是给浏览器的“提示”,不是强制约束。实测中:accept="image/*" 在 Chrome 桌面端能过滤非图片类型,但在 iOS Safari 中仍可点击“所有文件”绕过;某些安卓 WebView 也默认显示全部文件类型。更麻烦的是,用户可能手动修改文件扩展名(如把 malware.exe 改成 photo.jpg),accept 完全无法识别。
必须搭配 JavaScript 做 MIME 类型和后缀双重校验
在 change 事件里检查 file.type 和 file.name 才算真正落地:
-
file.type应匹配"image/jpeg"、"image/png"、"image/gif"、"image/webp"等常见类型(注意:Safari 可能返回空字符串,需 fallback 到后缀) - 用
file.name.toLowerCase().endsWith(".jpg")或正则/\.(jpe?g|png|gif|webp)$/i检查扩展名 - 若任一不满足,立即执行
input.value = ""清空选择,并提示用户
移动端调起相机时要加 capture 属性
想让 iOS/Android 用户点选后直接打开摄像头而不是相册,得加 capture="environment"(后置)或 capture="user"(前置)。但注意:capture 和 multiple 互斥,iOS 不支持同时使用;且 accept 必须保留,否则某些安卓机型可能无法触发摄像头。
立即学习“前端免费学习笔记(深入)”;
正确写法示例:<input type="file" accept="image/*" capture="environment">
后端永远要重验,前端校验只是体验优化
前端任何限制都可被绕过:fetch 请求可手动生成、FormData 可伪造、file.type 可篡改。所以服务端必须做三件事:
- 读取文件头(magic number)判断真实类型,而非依赖
Content-Type或扩展名 - 限制上传大小(比如
5 * 1024 * 1024字节),防止内存溢出 - 保存时重命名(如生成 UUID + 固定后缀
.jpg),禁止直接用用户传来的原始文件名
没做这三步,单靠前端 accept 就等于没设防。



















