uniapp引入jssdk(uniapp 实现人脸认证)
前言
对于前端来说 ,需要后端提供一个人脸识别接口 ,前端传入图片 ,接口识别并返回结果 ,如此看来 ,其实前端只需实现图片传入即可 ,但是其实不然 ,在传入图片时 ,需要进行以下几点操作:
判断图片格式 ,市场上比较常见的是.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 jpg let img_yuanshi = rem.tempFiles[num].path.substring(0, index); //截取图片原始路径 let d2 = new Date().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 = new plus.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; export default { 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 jpg let img_yuanshi = rem.tempFiles[num].path.substring(0, index); //截取图片原始路径 let d2 = new Date().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 = new plus.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, //默认9 sizeType: [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, //劳务人员Id userName: 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>创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!