前言
最近在做一个内嵌App的vueH5应用,项目中需要进行拍照上传人脸识别,然而App不提供原生拍照插件,故想使用H5的拍照。
首先调用H5拍照有两种方案:
1.使用H5Api:navigator.getUserMedia(constraints, successCallback, errorCallback);
2.使用input type=file 。
这里我们采用第二种方案,第一种兼容性不太好,并且没有第二种体验效果好。
代码实现
拿上input标签:
<input type="file" name="photo" accept=".jpg,.png" multiple>
默认样式比较丑,我们需要美化样式:
HTML:
<div class="face">
<input type="file" name="file" class="upload" @change="uploadImg">
<span class="span-txt">开始认证</span>
</div>
CSS:
.face{
margin-top: 20px;
position: relative;
.upload{
width: calc(100% - 40px);
height: 43px;
line-height: 43px;
opacity: 0;
position: absolute;
z-index: 22;
left: 0;
margin: auto;
right: 0;
}
.span-txt{
font-family: PingFangSC-Medium;
font-size: 16px;
color: #FFFFFF;
position: absolute;
left: 0;
margin: auto;
right: 0;
background: #CDAB6A;
width: calc(100% - 40px);
height: 43px;
line-height: 43px;
border-radius: 4px;
text-align: center;
}
}
美化后效果:
如同button一般,点击即可调用摄像头拍照,或者使用相册。
接下来就是使用处理图片进行上传
在uploadImg方法中处理系统拿到的图片,整体的思路就是:先把拿到的file文件去获取当前文件的角度方向,接着将文件转为reader对象,在reader的onload函数中将文件转为base64,然后把base64传入resetOrientation 方法中(矫正压缩图片),最后再将base64转为接口所需的blob对象传给后台。以下为拍照上传代码:
uploadImg(e) {
const vm = this;
let file = e.target.files[0]
let param = new FormData() // 创建form对象
let config = {
headers: {'Content-Type': 'multipart/form-data'}
}
//解决ios拍照照片自动旋转问题
getOrientation(file, function (orientation) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(evt){
const base64 = evt.target.result;
// 将图片旋转到正确的角度 并压缩
resetOrientation(base64, orientation, function (resultBase64) {
b64toBlob(resultBase64, function (blob) {
param.append('file', blob); // 通过append向form对象添加数据
//调用接口上传图片
return registerFace(param, config).then((res) => {
// 上传成功逻辑
})
});
});
}
});
}
拍照时图片矫正的问题
由于我的项目中设计到上传人脸,所以上传的照片必须是正的,然而在实测中发现,在ios中拍照上传后会传一个90° 反转的照片上去,导致后端无法识别这种照片,在安卓机上则不会。那么,如何解决呢?
思路很简单,我们在上传照片前先做判断:如果是反转了的照片,我们调整过来,如果不是,不做调整。
1.使用EXIF.js对图片进行角度判断;定义一个函数:
// 获取图片的角度
function getOrientation(file, callback) {
EXIF.getData(file, function () {
var orientation = EXIF.getTag(this, 'Orientation');
return callback(orientation);
});
}
2.使用canvas drawImage方法将图片调整正确角度,在回调函数中使用toDataURL导出调整后的图片base64对象。
关键代码 base64toBlob函数:
function b64toBlob(b64, onsuccess, onerror) {
let img = new Image();
img.onerror = onerror;
img.onload = function onload() {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
let width = img.width;
let height = img.height;
let ctx = canvas.getContext('2d');
let rate = 1;
ctx.drawImage(img, 0, 0, width, height, 0, 0, width * rate, height * rate);
canvas.toBlob(onsuccess);
};
img.src = b64;
}
关键代码 resetOrientation函数:
function resetOrientation(srcBase64, srcOrientation, callback) {
const img = new Image();
img.onload = function () {
const width = img.width,
height = img.height,
canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
// 判断图片尺寸压缩一定比率
const big = (img.width > img.height) ? img.width : img.height;
let rate = 1;
if (big > 840) {
rate = 840 / big;
}
canvas.width = width * rate;
canvas.height = height * rate;
// 安卓机不需要矫正图片
if(srcOrientation && srcOrientation !== 1){
// 判断图片方向,压缩并矫正
switch (srcOrientation) {
// 当图片旋转180°时
case 3:
ctx.rotate(Math.PI);
ctx.drawImage(this, -this.width * rate, -this.height * rate, this.width * rate, this.height * rate);
break;
// 当图片旋转90°时
case 6:
canvas.width = this.height * rate;
canvas.height = this.width * rate;
ctx.rotate(Math.PI / 2);
// (0,-imgHeight) 从旋转原理图那里获得的起始点
ctx.drawImage(this, 0, -this.height * rate, this.width * rate, this.height * rate);
break;
// 当图片旋转270°时
case 8:
canvas.width = this.height * rate;
canvas.height = this.width * rate;
ctx.rotate(3 * Math.PI / 2);
ctx.drawImage(this, -this.width * rate, 0, this.width * rate, this.height * rate);
break;
default:
ctx.drawImage(img, 0, 0, width, height, 0, 0, width * rate, height * rate);
}
}else {
ctx.drawImage(img, 0, 0, width, height, 0, 0, width * rate, height * rate);
}
// 返回 base64
callback(canvas.toDataURL('image/jpeg'));
};
img.src = srcBase64;
};
至此,可解决图片反转的问题。