- 小程序识别身份证,银行卡,营业执照,驾照
最近老是有同学问我小程序ocr识别的问题,就趁机研究了下,实现了小程序识别身份证,银行卡,驾照,营业执照,图片文字的功能。今天来给大家讲讲详细的实现流程。 先画一张流程图出来 [图片] 第一次看到这个流程图,可能有点萌,什么云开发,云函数。。。。 不要着急,我们接下来会一步步带大家实现。 先看下我们的页面和效果图。 [图片] 功能其实很简单,就是我们点对应的按钮后,去拍照或者去相册选择对应的图片。然后把图片上传到云存储,会有一个对应的图片url,然后把这个图片url传递到云函数,然后云函数里使用小程序的开发ocr能力,来识别图片,返回对应的信息回来。如下图所示,我们识别银行卡(身份证什么的就不演示了,涉及到石头哥个人隐私) [图片] 接下来就是代码的实现了。 一,首先要创建一个云开发的小程序项目 这里我前面文章有讲解过,就不再细说了,不会的同学去翻看下我之前的文章。或者看下我录制的 讲解视频 这里有一点需要注意的给大家说下 [图片] 二,创建一个简单的小程序页面 1,index.wxml如下 [图片] 2,index.js完整代码如下 [代码]Page({ //身份证 shenfenzheng() { this.photo("shenfenzheng") }, //银行卡 yinhangka() { this.photo("yinhangka") }, //行驶证 xingshizheng() { this.photo("xingshizheng") }, //拍照或者从相册选择要识别的照片 photo(type) { let that = this wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success(res) { // tempFilePath可以作为img标签的src属性显示图片 let imgUrl = res.tempFilePaths[0]; that.uploadImg(type, imgUrl) } }) }, // 上传图片到云存储 uploadImg(type, imgUrl) { let that = this wx.cloud.uploadFile({ cloudPath: 'ocr/' + type + '.png', filePath: imgUrl, // 文件路径 success: res => { console.log("上传成功", res.fileID) that.getImgUrl(type, res.fileID) }, fail: err => { console.log("上传失败", err) } }) }, //获取云存储里的图片url getImgUrl(type, imgUrl) { let that = this wx.cloud.getTempFileURL({ fileList: [imgUrl], success: res => { let imgUrl = res.fileList[0].tempFileURL console.log("获取图片url成功", imgUrl) that.shibie(type, imgUrl) }, fail: err => { console.log("获取图片url失败", err) } }) }, //调用云函数,实现OCR识别 shibie(type, imgUrl) { wx.cloud.callFunction({ name: "ocr", data: { type: type, imgUrl: imgUrl }, success(res) { console.log("识别成功", res) }, fail(res) { console.log("识别失败", res) } }) } }) [代码] 上面代码注释讲解的很清楚了,再结合我们的流程图,相信你可以看明白。 [图片] 三,重头戏来了,识别的核心代码是下面这个云函数 [图片] 云函数的完整代码也给大家贴出来 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async(event, context) => { let { type, imgUrl } = event switch (type) { case 'shenfenzheng': { // 识别身份证 return shenfenzheng(imgUrl) } case 'yinhangka': { // 识别银行卡 return yinhangka(imgUrl) } case 'xingshizheng': { // 识别行驶证 return xingshizheng(imgUrl) } default: { return } } } //识别身份证 async function shenfenzheng(imgUrl) { try { const result = await cloud.openapi.ocr.idcard({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } } //识别银行卡 async function yinhangka(imgUrl) { try { const result = await cloud.openapi.ocr.bankcard({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } } //识别行驶证 async function xingshizheng(imgUrl) { try { const result = await cloud.openapi.ocr.vehicleLicense({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } } [代码] 其实没什么特别的,就是用一个switch方法,根据用户传入的不同的type值,来实现不同的识别效果。 如用传入的type是‘ yinhangka’,我们就调用银行卡识别 [代码]try { const result = await cloud.openapi.ocr.bankcard({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } [代码] 进而把识别的结果返回给小程序端,如下图 [图片] 到这里我们就完整的实现了,小程序识别身份证,银行卡,行驶证的功能。至于别的更多的ocr识别,可以去看小程序官方文档,结合着我的这篇文章,相信你也可以轻松实现更多的图片识别。 [图片] 源码其实在上面都已经贴给大家了,如果你觉得不完整,想要完整的源码可以在文章底部留言或者私信我。
2019-10-30 - 小程序页面加载性能优化实践
思路来自:https://developers.weixin.qq.com/community/develop/article/doc/000c6e6f7f0ca83c2069e656351813 具体的优化方向和思路可以参考上面的文章,主要就是利用从触发路由到下一个页面onLoad的这段时间来做数据请求,实际上就是提前即将加载页面的数据请求,从而提升页面展现速度。 [图片] 下面是优化的实践方案(这个方案因为是用在旧项目上,所以设计成嵌入式的,对现有业务逻辑没什么影响): 前提 下面的优化方案有两个前提 路由方法封装(项目中要有统一的路由钩子,至少需要优化的页面跳转要有) 数据model层,我们称为service(在实践之前我们也是没有这一层的,可以对需要优化的页面添加service) 实践 首先我们是这样获取页面需要的数据的 [代码]DetailService.getData(this.options).then((result) => { // 渲染逻辑 }) [代码] 看下这个DetailService,有一个getData方法,实际上就是获取后端数据,然后返回一个Promise。(当然getData里可以做任何事情,包括取缓存等,可以结合缓存优化) [代码]import BaseService from '../base'; class DetailService extends BaseService { // 预取服务,对应serviceMap中key preGetName = 'DetailService'; /** * service数据体 * @param {*} query */ getData (query = {}) { // 这里返回一个Promise return request({ url: '/v1.0/data', data: rdata }); } } // 单例service export default new DetailService(); [代码] 到上面一步,跟我们的数据预取都没有什么关系,无非就是抽离了获取数据的方法而已,预取逻辑在BaseService中,里面做的事情就是在getData之前判断是否有预取缓存,有则直接使用,没有就发起真正的请求 [代码]import serviceMap from './main'; /** * service基类 */ class BaseService { constructor () { setTimeout(() => { // 装饰模式包装预取 const childGetData = this.getData.bind(this); this.getData = (query = {}, path = '') => { const data = this.checkData(path); if (data) { return data; } return childGetData(query); }; }, 0); } /** * 检查预取 * @param {*} path */ checkData (path) { const name = this.preGetName; path = path || getCurrentPages().slice(-1)[0].route; return serviceMap && serviceMap[path] && serviceMap[path][name] && serviceMap[path][name].cacheRes; } } export default BaseService; [代码] 上面一步有一个serviceMap,用于存储每个页面需要预取的请求(这里只应该是影响页面首屏渲染的关键请求),其中为防止内存泄漏和不必要的缓存影响,使缓存结果为单次消费即销毁的模式 [代码]const serviceMap = { // 页面路径对应的主渲染service,可以为多个 'pages/index/index': { DetailService: { // 要求登录的service不能用于首屏预取 isRequireLogin: false, method: DetailService } } }; /** * 添加一次性消费的缓存属性 */ (function cacheService () { for (const key in serviceMap) { if (serviceMap.hasOwnProperty(key)) { const pageService = serviceMap[key]; for (const k in pageService) { if (pageService.hasOwnProperty(k)) { const service = pageService[k]; Object.defineProperty(service, 'cacheRes', { get () { let val = this.v || null; this.v = null; return val; }, set (val) { this.v = val; } }); } } } } })(); export default serviceMap; [代码] 数据预取钩子 [代码]/** * 预执行页面主service * 预取钩子 * @param {*} url 页面path,必选(不传query则可以带参数) * @param {*} query 页面参数,可选(不传会尝试从path中解析) */ const preRunPageService = (url, query) => { let path = url; if (!query) { ({path, query = {}} = parseUrl(url)); } const page = path.replace(/^\//, ''); for (const key in serviceMap[page]) { if (serviceMap[page].hasOwnProperty(key)) { const service = serviceMap[page][key]; if (service.isRequireLogin) { // 登录后才执行预取,否则什么也不做 Login(() => { service.cacheRes = service.method.getData(query, page); }); } else { service.cacheRes = service.method.getData(query, page); } } } }; export default preRunPageService; [代码] 好了,以上就完成了绝大部分的封装工作,之后我们只需要将预取钩子添加到任何想要预取数据的地方就可以了,例如路由钩子中或者App onLaunch中: [代码]function goNextPage (path) { preRunPageService(path) wx.navigateTo(...) } [代码] 任何一个页面需要预取数据优化时只需要写一个service继承BaseService(实现model层的优势不止于此),并添加路由映射到serviceMap即可。 结语 以上是对数据预取思路的实践封装,对页面加载性能确实有一定的提升,由于是嵌入式的,项目中的可维护性也能接受。 但是任何优化都要结合实际情况来定,比如我们本来就有抽离model层(service)的想法,并且我们的页面大多都有1~2个主渲染请求,但是因为是旧项目,页面众多,因此最终决定只对高PV页面以及筛选出来合适的页面做上述优化。 任何意见建议,欢迎交流~
2019-12-03