图片上传服务器后被自动旋转

最近在处理移动端上传图片遇到一个问题:有时会出现图片翻转的问题,一般是翻转 90 度。后经一翻研究找到了问题所在,特在此记录一下。

问题描述

经过测试发现:webapp在iPhone手机在竖屏下拍摄图片,上传后会出现图片翻转;横屏不会出现这样的问题。部分Android手机也会出现类似的问题。原生的没有测试过。

问题分析

在这里必须要知道可交换图像文件格式(英语:Exchangeable image file format,官方简称Exif),是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。

利用exif.js读取照片的拍摄信息,详见 exif-js

这里主要用到Orientation属性。 说明如下:

旋转角度 参数
1
顺时针90° 6
逆时针90° 8
180° 3

通过Orientation属性判断图片是否翻转后,再用canvas把图片转正。

解决问题

假设已经引入exif.js,并通过input标签获得file对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// 纠正图片旋转方向
function correcctImageOrientation(file) {
var Orientation = null;

if (file) {
console.log("正在上传,请稍后...");
var rFilter = /^(image\/jpeg|image\/png)$/i; // 检查图片格式
if (!rFilter.test(file.type)) {
return;
}
// 获取照片方向角属性,用户旋转控制
EXIF.getData(file, function() {
EXIF.getAllTags(this);
Orientation = EXIF.getTag(this, 'Orientation');
});

var oReader = new FileReader();
oReader.onload = function(e) {
var image = new Image();
image.src = e.target.result;
image.onload = function() {
var expectWidth = this.naturalWidth;
var expectHeight = this.naturalHeight;
// 压缩图片。最大宽800,最大高1200
if (this.naturalWidth > this.naturalHeight && this.naturalWidth > 800) {
expectWidth = 800;
expectHeight = expectWidth * this.naturalHeight / this.naturalWidth;
} else if (this.naturalHeight > this.naturalWidth && this.naturalHeight > 1200) {
expectHeight = 1200;
expectWidth = expectHeight * this.naturalWidth / this.naturalHeight;
}
// 绘制canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = expectWidth;
canvas.height = expectHeight;
ctx.drawImage(this, 0, 0, expectWidth, expectHeight);
// base64 字符串
var base64 = null;
if(Orientation != "" && Orientation != 1){
switch(Orientation){
case 6:
// 需要顺时针(向左)90度旋转
rotateImg(this,'left',canvas);
break;
case 8:
//需要逆时针(向右)90度旋转
rotateImg(this,'right',canvas);
break;
case 3:
//需要180度旋转
rotateImg(this,'right',canvas);//转两次
rotateImg(this,'right',canvas);
break;
}
}

base64 = canvas.toDataURL("image/jpeg", 0.8);
// uploadImage(base64); // 上传base64的文件
// uploadImage(dataURLtoFile(base64, file.name)); // 通过FormData的file上传
// 用base64回显
$("#myImage").attr("src", base64);
};
};
oReader.readAsDataURL(file);
}
}

// 对图片旋转处理
function rotateImg(img, direction,canvas) {
// 最小与最大旋转方向,图片旋转4次后回到原方向
var min_step = 0;
var max_step = 3;
if (img == null)return;
// img的高度和宽度不能在img元素隐藏后获取,否则会出错
var height = img.height;
var width = img.width;
var step = 2;
if (step == null) {
step = min_step;
}
if (direction == 'right') {
step++;
// 旋转到原位置,即超过最大值
step > max_step && (step = min_step);
} else {
step--;
step < min_step && (step = max_step);
}
// 旋转角度以弧度值为参数
var degree = step * 90 * Math.PI / 180;
var ctx = canvas.getContext('2d');
switch (step) {
case 0:
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0);
break;
case 1:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, 0, -height);
break;
case 2:
canvas.width = width;
canvas.height = height;
ctx.rotate(degree);
ctx.drawImage(img, -width, -height);
break;
case 3:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, -width, 0);
break;
}
}

/**
* img的base64转File对象,并不是文件
* @param {String} dataurl 数据源
* @param {String} filename 保存的File对象名
*/
export function dataURLtoFile(dataurl, filename) {
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, { type: mime })
}

或者使用 fix-orientation来修复。

坚持原创技术分享,您的支持将鼓励我继续创作!