背景

通过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

  1. const reader = new FileReader()
  2. reader.onload = async function () {
  3. const buffer = reader.result
  4. const orientation = getOrientation(buffer)
  5. const image = await rotateImage(buffer, orientation)
  6. }
  7. reader.readAsArrayBuffer(file)

getOrientation

  1. function getOrientation(buffer) {
  2. // 建立一个 DataView
  3. const dv = new DataView(buffer)
  4. // 设置一个位置指针
  5. let idx = 0
  6. // 设置一个默认结果
  7. let value = 1
  8. // 检测是否是 JPEG
  9. if (buffer.length < 2 || dv.getUint16(idx) !== 0xFFD8 {
  10. return false
  11. }
  12. idx += 2
  13. let maxBytes = dv.byteLength
  14. // 遍历文件内容,找到 APP1, 即 EXIF 所在的标识
  15. while (idx < maxBytes - 2) {
  16. const uint16 = dv.getUint16(idx)
  17. idx += 2
  18. switch (uint16) {
  19. case 0xFFE1:
  20. // 找到 EXIF 后,在 EXIF 数据内遍历,寻找 Orientation 标识
  21. const exifLength = dv.getUint16(idx)
  22. maxBytes = exifLength - 2
  23. idx += 2
  24. break
  25. case 0x0112:
  26. // 找到 Orientation 标识后,读取 DDDDDDDD 部分的内容,并把 maxBytes 设为 0, 结束循环。
  27. value = dv.getUint16(idx + 6, false)
  28. maxBytes = 0
  29. break
  30. }
  31. }
  32. return value
  33. }

获取了Orientation后,通过canvas旋转图片

顺时针90

  1. compressCanvas.width = image.height;
  2. compressCanvas.height = image.width;
  3. compressCtx.clearRect(0, 0, compressCanvas.width, compressCanvas.height)
  4. compressCtx.translate(0, 0);
  5. compressCtx.rotate(90 * Math.PI / 180);
  6. compressCtx.drawImage(image, 0, -image.height, image.width, image.height);
  7. break;