h5移动端拍照上传需通过解析jpeg的exif orientation字段并在canvas中按规则旋转绘制来纠正方向。核心步骤:用piexifjs读取orientation值(1/3/6/8等),动态设置canvas宽高并应用translate+rotate变换,最后toblob上传;非jpeg跳过矫正,服务端应兜底二次校正。

在 H5 移动端实现拍照上传图片的自动 EXIF 角度旋转纠正,核心在于:**读取图片的 EXIF Orientation 信息,并在 Canvas 渲染时按需翻转/旋转画布,最终输出方向正确的图像数据**。浏览器原生 `` 拍照获取的图片(尤其是 iOS Safari)常带有 Orientation 标签,但直接显示或上传会忽略该标签,导致竖拍照片横着显示。
1. 检测并读取图片的 EXIF Orientation 值
不能依赖 `img.naturalWidth/naturalHeight` 或 CSS transform 判断方向——这些不反映原始 EXIF。必须解析二进制 JPEG 数据中的 APP1 段(EXIF 存储位置)。推荐使用轻量库 exif-js(注意:v2.3.0+ 支持 Promise,兼容性好)或更现代的 piexifjs(纯 JS、无依赖、支持读写)。
示例(使用 piexifjs):
import piexif from 'piexifjs';
<p>function getOrientation(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
try {
const exifObj = piexif.load(reader.result);
const orientation = exifObj?.["0th"]?.[piexif.ImageIFD.Orientation] || 1;
resolve(orientation);
} catch (e) {
resolve(1); // 无 EXIF 或解析失败,默认为正常方向
}
};
reader.onerror = reject;
reader.readAsArrayBuffer(file);
});
}
</p>2. 在 Canvas 中按 Orientation 进行矫正绘制
Orientation 共 8 种值(1~8),常用的是 1(正常)、3(180°)、6(顺时针90°)、8(逆时针90°)。矫正逻辑不是简单地用 CSS 旋转 img,而是将原始图像按规则绘制到 Canvas 上,使最终 canvas.toDataURL() 输出的方向正确。
关键步骤:
- 创建足够大的 canvas(宽高根据 orientation 动态交换)
- 用
ctx.save()/ctx.restore()隔离变换状态 - 对 orientation=6/8 的图,先平移原点再旋转,避免裁剪
简化矫正逻辑(适用于大多数场景):
function rotateImageToCorrect(ctx, img, orientation) {
const { naturalWidth: w, naturalHeight: h } = img;
let width = w;
let height = h;
<p>// 根据 orientation 设置 canvas 尺寸和变换
if ([6, 8].includes(orientation)) {
width = h;
height = w;
}
ctx.canvas.width = width;
ctx.canvas.height = height;</p><p>switch (orientation) {
case 1:
ctx.drawImage(img, 0, 0, w, h);
break;
case 3:
ctx.translate(w, h);
ctx.rotate(Math.PI);
ctx.drawImage(img, 0, 0, w, h);
break;
case 6:
ctx.translate(width, 0);
ctx.rotate(Math.PI / 2);
ctx.drawImage(img, 0, 0, w, h);
break;
case 8:
ctx.translate(0, height);
ctx.rotate(-Math.PI / 2);
ctx.drawImage(img, 0, 0, w, h);
break;
default:
ctx.drawImage(img, 0, 0, w, h);
}
}
</p>3. 完整流程:拍照 → 读 EXIF → Canvas 矫正 → 生成 Blob 上传
注意:iOS Safari 的 `` 拍照后,File 对象的 type 可能是 image/jpeg,但部分安卓机型可能返回 image/png(无 EXIF)。因此需判断是否为 JPEG 再解析 EXIF,否则跳过矫正。
关键处理链:
- 监听 input change,获取 file
- 检查 file.type.startsWith('image/jpeg'),非 JPEG 直接上传(不矫正)
- 调用 getOrientation(file) 获取 orientation
- 用 URL.createObjectURL 创建 img.src,onload 后调用 rotateImageToCorrect
- canvas.toBlob(blob => { /* 上传 blob */ }, 'image/jpeg', 0.9)
⚠️ 补充提示:
- Android Chrome 通常不写 EXIF Orientation(尤其系统相机直出),但微信内置浏览器、iOS Safari 严格保留
- 不要用 canvas.toDataURL() 后再转 blob——质量损失大且体积膨胀;优先用 toBlob
- 如需保持原始质量,可将矫正后的 canvas 绘制成同等分辨率 JPEG,并设置高质量(0.95~1.0)
4. 替代方案:服务端矫正(兜底建议)
前端矫正无法覆盖所有边缘情况(如 EXIF 损坏、非 JPEG 格式、WebP 等)。建议上传时附带原始 orientation 值(如 FormData 中加字段 exif_orientation=6),服务端用 Pillow(Python)或 ImageMagick 再次校验并统一转正,确保最终存储图像是方向一致的。











