背景

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; // TS内容 可不写
}
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')); // 先获取方向信息file为文件流
	     	
	     	// ——————————————————————————
			// 这里是图片文件处理过程,省略1w行代码...
			// ——————————————————————————
	
			// 开始利用piexif写图片方向信息
			let zeroth: IExifElement = {};
	        zeroth[TagValues.ImageIFD.Orientation] = orientation;
	        
	        let exifObj: IExif = { '0th': zeroth }; // 重新写方向
	        let exifStr = Piexif.dump(exifObj); //获取需要写信息的合法字符串格式
	        let resultBase64 = Piexif.insert(exifStr, file); //将exif信息插入到base64中,此时的file为处理后的文件流,非base64,服务端如果需要base64格式则直接上传即可
			
			// 若服务器需要文件流格式(blob),仍需要转化为blob
			const blobfilr = base64ToBlob(resultBase64);  
			
			// 发送ajax  
	 	});

    }, [])
    
	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

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐