在项目中有个图片上传的组件(vue项目)
<input
ref="input"
type="file"
multiple="false"
accept="image/*"
@change="handleChange">
在ios中点击的时候会有一个拍照的选项,但是!!!拍照的时候手机拿的方向不同,上传的图片会出现不同角度的旋转,具体现象如下:
所以我们需要将旋转了的图片再转回来!!!
具体实现思路:
1. 知道图片文件当前的旋转情况
2. 将图片文件旋转指定角度
3. 上传旋转后的文件流
每张图片文件都有其对应的数据信息,这里我们通过第三方库exif-js
来获取图片的一些信息
(1) 安装依赖
npm i exif-js -D
(2) 写一个获取图片信息的通用方法
import Exif from 'exif-js';
/**
* @desc 获取图片信息,使用exif.js库,具体用法请在github中搜索
* @param {Object} file 上传的图片文件
* @param {String} tag 需要获取的信息 例如:'Orientation'旋转信息
* @return {Promise} 读取是个异步操作,返回指定的图片信息
*/
export const getImageTag = (file, tag) => {
if (!file) return 0;
return new Promise((resolve, reject) => {
/* eslint-disable func-names */
// 箭头函数会修改this,所以这里不能用箭头函数
Exif.getData(file, function () {
const o = Exif.getTag(this, tag);
resolve(o);
});
});
};
(3) 调用该方法获取图片的旋转信息
...
// file是表单上传时通过e.target.file获取到的文件流
const or = await getImageTag(file, 'Orientation');
安卓手机
下or的值为undefiend
或0
,安卓手机不会出现旋转情况,所以不用考虑
通过input file选择的是个文档流我们无法处理,所以这里我们通过FileReader将文档流转为base64,然后将base64渲染到canvas上,对canvas进行旋转
(1) 将文件流转为base64格式
// 使用FileReader读取文件流,file为上传的文件流
const reader = new FileReader();
reader.readAsDataURL(file);
/* eslint-disable func-names */
// 箭头函数会改变this,所以这里不能用肩头函数
reader.onloadend = function () {
// this.result就是转化后的结果
const result = this.result;
// 将base64添加到图片标签上
const img = new Image();
img.src = result;
img.onload = function () {
// 这里添加旋转图片的代码
};
};
(2) 在img.onload中添加旋转图片
const self = this;
...
img.onload = function () {
// 获取旋转后的图片
const data = self.getRotateImg(img, or);
// 如果上传接口不支持base64,则这里需要将base64转为文档流
const f = dataURLtoFile(data);
// 调用接口,上传图片
}
getRotateImg方法
/**
* @desc 获取旋转后的图片
* @param {Object} img 图片文件
* @param {Number} or 旋转信息
*/
getRotateImg(img, or) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 图片原始大小
const width = img.width;
const height = img.height;
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
switch (or) {
case 6: // 顺时针旋转90度
rotateImg(img, 'right', canvas);
break;
case 8: // 逆时针旋转90度
rotateImg(img, 'left', canvas);
break;
case 3: // 顺时针旋转180度
rotateImg(img, 'right', canvas, 2);
break;
default:
break;
}
}
....
/**
* @desc 旋转canvas,会对源数据canvas进行修改
* @param {Object} img 图片文件
* @param {String} dir 方向 left逆时针|right顺时针
* @param {Object} canvas 画布
* @param {Number} s 向指定方向旋转几步,1步为90度
*/
export const rotateImg = (img, dir = 'right', canvas, s = 1) => {
const MIN_STEP = 0;
const MAX_STEP = 3;
const width = canvas.width || img.width;
const height = canvas.height || img.height;
let step = 0;
if (dir === 'right') {
step += s;
step > MAX_STEP && (step = MIN_STEP);
} else {
step -= s;
step < MIN_STEP && (step = MAX_STEP);
}
const degree = step * 90 * Math.PI / 180;
const ctx = canvas.getContext('2d');
switch (step) {
case 1:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, 0, -height, width, height);
break;
case 2:
canvas.width = width;
canvas.height = height;
ctx.rotate(degree);
ctx.drawImage(img, -width, -height, width, height);
break;
case 3:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, -width, 0, width, height);
break;
default:
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
break;
}
};
base64转文件流方法
/**
* @desc 将base64的图片转为文件流
* @param {String} dataUrl base64数据
* @return {Object} 文件流
*/
export const dataURLtoFile = (dataUrl) => {
const filename = `img${Date.now()}`;
const arr = dataUrl.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
};
调用自己项目中的post方法
完整的代码放到github上,可以访问以下地址查看
https://github.com/yywang95/h5ImageUploader