背景
input标签调起ios原生摄像头拍照时,上传照片发现照片向左旋转了90度
旋转的原因:
手机拍照会给图片添加一个Orientaion信息(即拍照方向),如下: 用ios手机拍照,系统会给图片加上一个方向的属性, ios相机默认的拍照方向是后摄Home键在右为正,前摄Home键在左为正。
1代表正常的拍摄角度,ios横屏下拍摄、安卓机无论横屏竖屏拍摄,Orientaion的值都为1;但是ios竖屏拍摄,Orientaion的值为6,即竖着拍出的照片被添加了一个顺时针旋转90°的拍照方向,显示的时候其实就是横着拍的照片顺时针旋转90°而成。当我们对拍出来的照片进行处理后(比如压缩),这个拍摄方向Orientaion信息就会丢失,显示的效果自然回到横屏状态,看起来像是逆时针旋转了90°。
受之于鱼不如授之于渔 我提出三种解决思路
第一种解决思路:
1、利用exif-js获取file拍摄方向信息Orientation 2、创建canvas,ctx = canvas.getContext(‘2d’); 3、根据Orientation,经过ctx.rotate(旋转)、 ctx.drawImage(手动绘图) 4、canvas.toBlob 创建文件流,再传给服务端
这也是网上的大神通用的解决办法
- 缺点(推荐指数:3星)
- 很多人卡在了第一步,获取到的Orientation=undefined; 首先获取图片信息只对拍摄方式的照片生效,不要在本地上传图片进行调试;其次获取Orientation时,要在回调函数内获取,如下代码
- 需要熟悉canvas知识
- 第三部rotate、drawImage方法如果没有掌握的很好,最后的绘图步骤会让你抓狂
- 第四部创建的文件流会超级大,需要很久的上传时长,尽管可以控制转换质量使之变小,但也会大大失桢
// 只对即时拍摄的照片生效,file为文件流,非base64
ExifObj.getData(file, function () {
const Orientation = ExifObj.getTag(file, 'Orientation');
// 在这里获取
});
// 不要在这里获取
第二种解决思路:
如果没有特殊要求,请不要对图片进行二次处理,例如进行压缩,最简单快捷地解决问题;
第三种解决思路:(推荐指数:5星)
思路:既然经处理后的图片方向信息丢失,那我们可以手动再次添加方向不就解决问题了嘛
- 如果图片必须经过处理,在处理前请先利用exif-js缓存照片方向信息
- 处理后利用piexif手动对处理后的文件添加方向信息
- 最后向服务器发送文件流或base64信息-保存
- 缺点:最后添加的方向信息可能会使图片的size增加,但完全规避了方法一繁琐的步骤及开发成本
npm i exif-js
npm i piexif
...
import React, { useState, useEffect, FC } from 'react';
import Exif from 'exif-js';
import * as piexif from 'piexif';
import { IExif, IExifElement, TagValues } from 'piexif';
interface IProps {
history: any;
}
const AppView: FC<IProps> = props => {
const ExifObj = Exif;
const Piexif = piexif;
const [orientation, setOrientation] = useState<number>(1);
useEffect(() => {
ExifObj.getData(file, function () {
setOrientation(ExifObj.getTag(file, 'Orientation'));
let zeroth: IExifElement = {};
zeroth[TagValues.ImageIFD.Orientation] = orientation;
let exifObj: IExif = { '0th': zeroth };
let exifStr = Piexif.dump(exifObj);
let resultBase64 = Piexif.insert(exifStr, file);
const blobfilr = base64ToBlob(resultBase64);
});
}, [])
const base64ToBlob = base64 => {
const parts = base64.split(';base64,');
const contentType = parts[0].split(':')[1];
const raw = window.atob(parts[1]);
const rawLength = raw.length;
const uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; i += 1) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], { type: contentType });
};
}
如果真的解决了你的问题或对你有启发,回来点个赞吧
参考文献: 【npm官网】https://www.npmjs.com/package/piexif 【前端开发仓库】http://code.ciaoca.com/javascript/exif-js/ 【zoukankan】http://t.zoukankan.com/ranyonsue-p-10830688.html
|