最简方案是<input type="file" accept="image/*" capture>,accept限定图片类型,capture强制调起摄像头;iOS需加capture="environment"或"user"指定镜头,Android兼容性较差;上传前须手动处理EXIF方向、canvas旋转压缩,且不可JS赋值file input,需用FormData提交。

input type="file" accept="image/*" capture 是最简方案
移动端调用摄像头拍照上传,不需要 JS 拦截或额外权限申请,input 原生就支持。关键在两个属性:accept="image/*" 告诉浏览器只接受图片,capture(无值)强制触发摄像头而非图库。
常见错误是只写 accept="image/jpeg" 或漏掉 capture,结果在 iOS 上仍弹出图库选择;Android 部分机型(如旧版 MIUI)则可能忽略 capture,但加了总比不加强。
-
<input type="file" accept="image/*" capture>—— 最小可用形态 -
<input type="file" accept="image/*" capture="environment">—— 强制后置摄像头(iOS 16+ / Android 大部分新机型支持) -
<input type="file" accept="image/*" capture="user">—— 强制前置(自拍场景)
capture 属性值在不同平台表现不一致
capture 是布尔属性,但加值后行为有差异:iOS 仅识别 capture="environment" 和 capture="user",且只在 Safari 中生效;Android Chrome 支持但依赖系统相机 App 是否响应 intent,部分定制 ROM(如华为 EMUI)会静默降级为图库选择。
不要依赖 capture 100% 触发摄像头。如果业务强要求拍照(比如实名认证),得配合 JS 检测并引导用户手动切换:
立即学习“前端免费学习笔记(深入)”;
- 用
navigator.mediaDevices.enumerateDevices()查是否有videoinput设备(但移动端常返回空,不可靠) - 更实际的做法:默认渲染带
capture的input,再加一个「拍照失败?点此重试」按钮,点击后用MediaRecorder手动拉起流 —— 但这需要https+ 用户主动点击授权
上传前预览和压缩必须自己做
原生 input 拍完直接拿到 File 对象,但不会自动压缩、旋转或裁剪。iOS 拍照后照片常带 EXIF Orientation,直接上传会导致服务端解析出错(比如竖图横着存)。
关键步骤要自己补上:
- 用
URL.createObjectURL(file)生成预览 URL,设给<img>标签显示 - 读取 EXIF(推荐
exif-js或现代替代piexifjs),根据Orientation值 canvas 旋转画布 - 调用
canvas.toBlob(callback, 'image/jpeg', 0.8)压缩并转回 Blob,再用new File([blob], 'photo.jpg', {type:'image/jpeg'})重建 File 对象 - 注意:Safari 对
toBlob支持差,得 fallback 到toDataURL再 base64 → blob
表单 submit 时 file input 的 value 不可被 JS 修改
你不能用 JS 给 <input type="file"> 赋值(比如 el.value = newFile),这是浏览器安全限制。所以“拍照 → 处理 → 自动提交”流程必须绕过直接赋值:
- 处理完得到新
File后,用FormData.append('photo', file)构造数据 - 禁用原生表单提交,改用
fetch或XMLHttpRequest发送 - 如果必须走传统 form submit,只能把处理后的文件转成 base64 存进隐藏
<input type="hidden">,后端再解码 —— 但大图 base64 体积膨胀 33%,慎用
EXIF 修正和 canvas 旋转是容易被跳过的环节,尤其测试时用安卓机拍没问题,上线后 iOS 用户大量反馈照片倒着,就是没处理 Orientation。别信“拍出来什么样就传什么样”。



















