背景
通过h5上传图片,有时候上传的图片会翻转,需要把图片的方向正过来。
EXIF 的格式
DataView
DataView 可以定义一个复合视图。比如 Uint8Array 定义的视图,所以元素都是 无符号8位整数,而 DataView 定义的视图,可以第一个字节是 Uint8, 第二个字节是 Int16 等,且可以自定义字节序。
EXIF 全称 (Exchangeable Image File Format) 照相机拍摄图像一般都由两大部分组成,一部分是数据本身,它记录了每个像素的颜色值,另外一部分是文件头,这里面记录着形如图像的宽度,高度等信息。所讨论的方向信息便是被存储于文件头中。更为具体一些
可交换图像文件格式常被简称为Exif(Exchangeable image file format),是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据… Exif可以附加于JPEG、TIFF、RIFF等文件之中。
当然,Exif 中的 Orientation 属性,取决于拍照的设备是否拥有方向传感器。不过根据我的了解,目前大部分数码拍照设备都支持记录方向。1 是默认值,2、4、5、7 表示照片进行了翻转。一般情况下,取值应该是 1、3、6、8 中的一种。
获取EXIF旋转照片
如何从图片中获取 Exif 信息,各个语言都有封装好的代码可以直接使用。推荐一个比较好用的库,api也比较齐全 exif-js,它做法分为两步,
第一步,首先判断 img.src,如果是 Data URI(即base64)则通过 atob 函数转解码base-64编码字符串换为二进制,如果是 Object URL【blob】 通过 readAsArrayBuffer 函数读取二进制,反之则通过 Ajax 获取原始二进制;第二步,是从原始数据不同位置匹配获取相关信息,基本是体力活了。
不依赖库 获取Exif的Orientation
file转arrayBuffer
const reader = new FileReader()
reader.onload = async function () {
const buffer = reader.result
const orientation = getOrientation(buffer)
const image = await rotateImage(buffer, orientation)
}
reader.readAsArrayBuffer(file)
getOrientation
function getOrientation(buffer) {
// 建立一个 DataView
const dv = new DataView(buffer)
// 设置一个位置指针
let idx = 0
// 设置一个默认结果
let value = 1
// 检测是否是 JPEG
if (buffer.length < 2 || dv.getUint16(idx) !== 0xFFD8 {
return false
}
idx += 2
let maxBytes = dv.byteLength
// 遍历文件内容,找到 APP1, 即 EXIF 所在的标识
while (idx < maxBytes - 2) {
const uint16 = dv.getUint16(idx)
idx += 2
switch (uint16) {
case 0xFFE1:
// 找到 EXIF 后,在 EXIF 数据内遍历,寻找 Orientation 标识
const exifLength = dv.getUint16(idx)
maxBytes = exifLength - 2
idx += 2
break
case 0x0112:
// 找到 Orientation 标识后,读取 DDDDDDDD 部分的内容,并把 maxBytes 设为 0, 结束循环。
value = dv.getUint16(idx + 6, false)
maxBytes = 0
break
}
}
return value
}
获取了Orientation后,通过canvas旋转图片
顺时针90
compressCanvas.width = image.height;
compressCanvas.height = image.width;
compressCtx.clearRect(0, 0, compressCanvas.width, compressCanvas.height)
compressCtx.translate(0, 0);
compressCtx.rotate(90 * Math.PI / 180);
compressCtx.drawImage(image, 0, -image.height, image.width, image.height);
break;