前言
对于前端来说,需要后端提供一个人脸识别接口,前端传入图片,接口识别并返回结果,如此看来,其实前端只需实现图片传入即可,但是其实不然,在传入图片时,需要进行以下几点操作:
- 判断图片格式,市场上比较常见的是
.jpg
、.jpeg
、.png
- 计算文件大小,一般要求不超过5MB
- 对图片进行base64加密
其实前2点具体要看接口要求,但是第3点,是实现人脸识别必备步骤,下文重点讲述一下移动端实现人脸识别的base64加密方法
问题
项目主要使用的技术栈是uniapp,uniapp的优点是上手快,基于vue开发,但缺点也很明显,多环境兼容导致兼容性较差,真机调试和运行较慢。比如h5端可以轻松实现base64加密,但是安卓环境完全不行,因为本地上传图片时,会返回一个blob流,但是uniapp的blob流是以http://localhost…(安卓环境无法识别localhost) 开始,导致无法进行base64加密
解决办法
经过多方实现后,借用html5+ api的多个结合方法(
plus.zip.compressImage
、
plus.io.resolveLocalFileSystemURL
、
plus.io.FileReader
)实现加密,主要代码如下:
//app压缩图片 用for循环 来处理图片压缩 的问题,原因是 plus.zip.compressImage 方法 是异步执行的,for循环很快, 同时手机可执行的压缩方法有限制:应该是3个吧。超出直接就不执行了。所以 原理就是 在图片压缩成功后 继续 回调 压缩函数。 以到达循环压缩图片的功能。app_img(num, rem){let that =this;let index = rem.tempFiles[num].path.lastIndexOf('.');//获取图片地址最后一个点的位置let img_type = rem.tempFiles[num].path.substring(index +1, rem.tempFiles[num].path.length);//截取图片类型如png jpglet img_yuanshi = rem.tempFiles[num].path.substring(0, index);//截取图片原始路径let d2 =newDate().getTime();//时间戳//压缩图片
plus.zip.compressImage({src: rem.tempFiles[num].path,//你要压缩的图片地址dst: img_yuanshi + d2 +'.'+ img_type,//压缩之后的图片地址(注意压缩之后的路径最好和原生路径的位置一样,不然真机上报code-5)quality:70//[10-100]},function(e){//压缩之后路径转base64位的//通过URL参数获取目录对象或文件对象
plus.io.resolveLocalFileSystemURL(e.target,function(entry){// 可通过entry对象操作test.html文件
entry.file(function(file){//获取文件数据对象var fileReader =newplus.io.FileReader();// 文件系统中的读取文件对象,用于获取文件的内容//alert("getFile:" + JSON.stringify(file));
fileReader.readAsDataURL(file);//以URL编码格式读取文件数据内容
fileReader.onloadend=function(evt){//读取文件成功完成的回调函数
that.base64Img = evt.target.result.split(',')[1];//拿到‘data:image/jpeg;base64,‘后面的
console.log('that.base64Img', that.base64Img);// rem.tempFiles[num].Base64_Path = evt.target.result.split(',')[1];};});});// that.base64Img = that.base64Img.concat(rem.tempFiles[num]);// 【注意】在此人脸认证中,只会传一张图片,故不考虑多张图片情况//利用递归循环来实现多张图片压缩// if (num == rem.tempFiles.length - 1) {// return;// } else {// that.app_img(num + 1, rem);// }},function(error){
console.log('Compress error!');
console.log(JSON.stringify(error));
uni.showToast({title:'编码失败'+ error
});});},
详细实现思路
其实对于uniapp实现人脸识别功能来讲,大概要经过这么几个步骤
onImage()
:打开手机相册上传图片,获取blob流(本地临时地址)#ifdef APP-PLUS
/#ifndef APP-PLUS
:判断系统环境,是h5还是安卓环境,然后在进行图片压缩和加密,具体实现代码如下:
//#ifdef APP-PLUS//图片压缩
that.app_img(0, res);//#endif// #ifndef APP-PLUS
that.blobTobase64(res.tempFilePaths[0]);// #endif
app_img()
/blobTobase64()
:对要识别的图片进行base64加密onSave()
—>upImage()
:附件上传,并处理识别信息
具体代码
<!-- 人脸认证 --><template><view><view class="u-margin-30 text-center"><u-avatar size="600":src="imageSrc"></u-avatar></view><view class="u-margin-60"><u-button type="primary"class="u-margin-top-60" @click="onImage">{{!imageSrc ?'拍照':'重拍'}}</u-button><!--<u-button type="primary"class="u-margin-top-30">重拍</u-button>--><u-button type="primary"class="u-margin-top-50" @click="onSave">保存</u-button></view><u-toast ref="uToast"/></view></template><script>import{ registerOrUpdateFaceInfo, UpdateLaborPersonnel }from'@/api/mww/labor.js';import{ UploadByProject }from'@/api/sys/upload.js';import{ sysConfig }from'@/config/config.js';import storage from'store';import{ACCESS_TOKEN}from'@/store/mutation-types';exportdefault{name:'face-authentication',data(){return{imageSrc:'',lastData:{},base64Img:'',base64:''};},onLoad(option){this.lastData =JSON.parse(decodeURIComponent(option.lastData));
console.log('前一个页面数据',this.lastData);
uni.setNavigationBarTitle({title:this.lastData.CnName +'-人脸认证 '});},methods:{onSave(){if(!this.imageSrc){this.$refs.uToast.show({title:'请先拍照',type:'error'});}// 人脸上传,附件上传,劳务人员信息修改this.upImage();},// h5压缩图片的方式,url为图片流blobTobase64(url){
console.log('进来了2', url);let imgFile = url;let _this =this;
uni.request({url: url,method:'GET',responseType:'arraybuffer',success:res=>{let base64 = uni.arrayBufferToBase64(res.data);//把arraybuffer转成base64
_this.base64Img ='data:image/jpeg;base64,'+ base64;//不加上这串字符,在页面无法显示}});},//app压缩图片 用for循环 来处理图片压缩 的问题,原因是 plus.zip.compressImage 方法 是异步执行的,for循环很快, 同时手机可执行的压缩方法有限制:应该是3个吧。超出直接就不执行了。所以 原理就是 在图片压缩成功后 继续 回调 压缩函数。 以到达循环压缩图片的功能。app_img(num, rem){let that =this;let index = rem.tempFiles[num].path.lastIndexOf('.');//获取图片地址最后一个点的位置let img_type = rem.tempFiles[num].path.substring(index +1, rem.tempFiles[num].path.length);//截取图片类型如png jpglet img_yuanshi = rem.tempFiles[num].path.substring(0, index);//截取图片原始路径let d2 =newDate().getTime();//时间戳//压缩图片
plus.zip.compressImage({src: rem.tempFiles[num].path,//你要压缩的图片地址dst: img_yuanshi + d2 +'.'+ img_type,//压缩之后的图片地址(注意压缩之后的路径最好和原生路径的位置一样,不然真机上报code-5)quality:70//[10-100]},function(e){//压缩之后路径转base64位的//通过URL参数获取目录对象或文件对象
plus.io.resolveLocalFileSystemURL(e.target,function(entry){// 可通过entry对象操作test.html文件
entry.file(function(file){//获取文件数据对象var fileReader =newplus.io.FileReader();// 文件系统中的读取文件对象,用于获取文件的内容//alert("getFile:" + JSON.stringify(file));
fileReader.readAsDataURL(file);//以URL编码格式读取文件数据内容
fileReader.onloadend=function(evt){//读取文件成功完成的回调函数
that.base64Img = evt.target.result.split(',')[1];//拿到‘data:image/jpeg;base64,‘后面的
console.log('that.base64Img', that.base64Img);// rem.tempFiles[num].Base64_Path = evt.target.result.split(',')[1];};});});// that.base64Img = that.base64Img.concat(rem.tempFiles[num]);// 【注意】在此人脸认证中,只会传一张图片,故不考虑多张图片情况//利用递归循环来实现多张图片压缩// if (num == rem.tempFiles.length - 1) {// return;// } else {// that.app_img(num + 1, rem);// }},function(error){
console.log('Compress error!');
console.log(JSON.stringify(error));
uni.showToast({title:'编码失败'+ error
});});},// 打开手机相机相册功能onImage(){const that =this;// 安卓系统无法默认打开前置摄像头,具体请看下面app-plus原因,
uni.chooseImage({count:1,//默认9sizeType:['original','compressed'],//可以指定是原图还是压缩图,默认二者都有sourceType:['camera'],// 打开摄像头-'camera',从相册选择-'album'success:function(res){
console.log('文件结果', res);if(res.tempFilePaths.length >0){// Blob流地址
that.imageSrc = res.tempFilePaths[0];//#ifdef APP-PLUS//图片压缩
that.app_img(0, res);//#endif// #ifndef APP-PLUS
that.blobTobase64(res.tempFilePaths[0]);// #endif}else{
that.$refs.uToast.show({title:'无文件信息',type:'error'});}},fail:function(res){
console.log('失败了', res.errMsg);
that.$refs.uToast.show({title: res.errMsg,type:'error'});}});// #ifdef APP-PLUS// console.log('app环境了');// 指定要获取摄像头的索引值,1表示主摄像头,2表示辅摄像头。如果没有设置则使用系统默认主摄像头。// 平台支持【注意注意注意】// Android - 2.2+ (不支持) :// 暂不支持设置默认使用的摄像头,忽略此属性值。打开拍摄界面后可操作切换。// iOS - 4.3+ (支持)// var cmr = plus.camera.getCamera(1);// var res = cmr.supportedImageResolutions[0];// var fmt = cmr.supportedImageFormats[0];// console.log('Resolution: ' + res + ', Format: ' + fmt);// cmr.captureImage(// function(path) {// alert('Capture image success: ' + path);// },// function(error) {// alert('Capture image failed: ' + error.message);// },// { resolution: res, format: fmt }// );// #endif},// 上传附件至[人脸认证]服务器upImage(){if(!this.base64Img){this.$refs.uToast.show({title:'无图片信息',type:'error'});return;}const params ={identityId:this.lastData.IdCard,//身份证号码imgInfo:this.base64Img,//头像采用base64编码userId:this.lastData.Id,//劳务人员IduserName:this.lastData.CnName //劳务姓名};
uni.showLoading();registerOrUpdateFaceInfo(params).then(res=>{if(res.success){this.$refs.uToast.show({title:'认证成功',type:'success'});// 上传至附件服务器+修改劳务人员信息this.uploadFile();}else{this.$refs.uToast.show({title:'认证失败,'+ res.message,type:'error'});
uni.hideLoading();}}).catch(err=>{
uni.hideLoading();
uni.showModal({title:'提示',content: err
});});},// 上传附件至附件服务器uploadFile(){const obj ={project:this.lastData.OrgCode ||this.$store.getters.projectCode.value,module:'mww.personnelCertification',segment:this.lastData.OrgCode,businessID:this.lastData.Id,storageType:1};let str =`project=${obj.project}&module=${obj.module}&segment=${obj.segment}&businessID=${obj.businessID}&storageType=${obj.storageType}`;
console.log('str', str);// const url = '';// console.log('url', url);// const formData = new FormData();// formData.append('file', this.imageSrc, '.png');// UploadByProject(str, formData).then(res => {// if (res.success) {// this.$refs.uToast.show({// title: '上传成功',// type: 'success'// });// } else {// this.$refs.uToast.show({// title: res.message,// type: 'error'// });// }// });const token = uni.getStorageSync(ACCESS_TOKEN);const that =this;// 需要使用uniapp提供的api,因为that.imageSrc的blob流为地址头为localhost(本地临时文件)
uni.uploadFile({url:`${sysConfig().fileServer}/UploadFile/UploadByProject?${str}`,filePath: that.imageSrc,formData:{...obj
},header:{// 必须传token,不然会报[系统标识不能为空]authorization:`Bearer ${token}`},name:'file',success:res=>{
that.$refs.uToast.show({title:'上传成功',type:'success'});
that.lastData.CertificationUrl = res.data[0].virtualPath;
that.lastData.Certification =1;
that.updateLaborPersonnel();},fail:err=>{
console.log('上传失败了', err);
that.$refs.uToast.show({title:'上传失败,'+ err,type:'error'});
uni.hideLoading();}});},// 修改劳务人员信息updateLaborPersonnel(){UpdateLaborPersonnel(this.lastData).then(res=>{if(res.success){this.$refs.uToast.show({title:'修改成功',type:'success'});// uni.showToast({// title: '成功了'// });setTimeout(()=>{
uni.navigateBack({delta:1});},800);}else{this.$refs.uToast.show({title:'修改失败,'+ res.message,type:'error'});}}).finally(()=>{
uni.hideLoading();});}}};</script><style scoped lang="less"></style>
版权归原作者 边中之城 所有, 如有侵权,请联系我们删除。