场景
用户在参加展会时会互相交换名片,这些名片后续需要录入到系统中,作为一个客户或者供应商存在。如果使用人工手动输入上传会比较耗时,因而需要一个将名片拍照后进行批量识别并直接导入到系统的功能。
解决思路
1、文件上传
拍好的名片照片需要进行上传,可以通过单张上传或者打成压缩包上传。若采用单张上传,那么文件的上传需要做成支持多选的功能,这样的话就会面临需要上传的文件会有几千张,如果做多选的话,我们的上传控件为了考虑性能问题,一定会做选择文件个数的限制,这样就会导致用户需要频繁操作,同时可能会造成很多的请求。鉴于以上原因此处选择压缩包分片上传,防止单个压缩包过大导致上传失败。
在技术上,此处选择阿里云的 对象存储OSS 来处理打包文件的分片上传,以下为主要代码:
📎aliyun-oss-sdk-5.3.1.min.js📎es6-promise.min.js📎md5.js📎uploadFile.js
<html><head><title></title><scripttype="text/javascript"src="/aliyun-oss-sdk-5.3.1.min.js"></script><scripttype="text/javascript"src="/es6-promise.min.js"></script><scripttype="text/javascript"src="/jSignature/md5.js"></script><scripttype="text/javascript"src="/mumu.file.js"></script></head><body><inputtype="file"name="file"title="上传文件"text="上传文件"allowexts="zip"onchange=""><p><buttontype="button"name="uploadFile"class="hide">上传文件</button></p><scripttype="text/javascript">varmyFiles= []; varsaveFileUrl="/add/upload/file/url";//上传文件成功后回调url//设置OSS上传的ststoken信息MyFile.stsTokenObj= { region: "oss-cn-qingdao",//替换你的bucket所在的区域accessKeyId: "your-AccessKeyId",//替换为你的accesskeyaccessKeySecret: "your-AccessKeySecret",//替换为你的accesskeySeceretstsToken: "your-sts-Token",//替换为你的ststokenbucket:"your-bucket-name",//替换为你的bucket那么secure:true }; //设置文件上传的路径MyFile.uploadFilePath="/my/file/path/"; //设置文件控件的变化$("body").delegate("input[type=file]", "change", function () { //上传文件时或者更换文件时,可进行文件类型、文件大小的验证for (vari=0; i<this.files.length; i++) { myFiles.push(this.files[i]); } }); //上传文件$("button[name=uploadFile]").click(function () { $(this).attr("disabled", "disabled"); letcurButton=$(this); varupLoadedCount=0; varfileUploadPromise= (file, index) => {//执行上传letpromise=newPromise((resolve, reject) => { varcallback=function (res) { varform=newFormData(); form.append("file", res.name);//上传文件后在oss的文件路径form.append("originName", res.originName);//上传的文件的本地名称$.ajax({ type: "post", url: saveFileUrl,//上传文件成功后回调地址data: form, processData: false, contentType: false, success: function (res) { curButton.removeAttr("disabled"); if (res.success) { alert("上传成功"); } else{ alert("上传失败"); } }, error: function (res) { curButton.removeAttr("disabled"); alert("上传失败"); } }); }; varuploadProgress=function (p) { console.log(p); returnfunction (done) { done(); } }; MyFile.applyTokenDo(MyFile.uploadFile, file, callback, uploadProgress); }); returnpromise; }; varhandleFiles= (files) => { letfilesPromise=files.map((file, index) =>fileUploadPromise(file, index)); Promise.all(filesPromise) .then(data=> { data.map((image, index) =>console.log("success")) }) }; handleFiles(myFiles); }); </script></body></html>
这里分片上传文件到 OSS 使用了 OSS 的官方sdk,大家可参考阿里云官网上的 OSS SDK下载最新的开发包。上传时的 AccessKey 和 AccessSecret 为使用 STS 方式获取的临时 token,这样可以保证原密钥的安全性。这里就不再说明了。
2、保存文件数据、发送队列
在文件上传成功后,可将上传的文件信息保存到数据库中,比如,可设计一个文件记录表:
CREATETABLE `tbfile` ( `id` int(11)unsignedNOTNULL AUTO_INCREMENT, `file_path` varchar(300) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '文件路径', `createtime` datetime DEFAULT NULL COMMENT '上传时间', `origin_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '原始名', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='上传文件';
考虑到图片识别的速度会很慢,如果立即识别可能会导致用户的浏览器连接超时,当压缩包上传成功后,此处使用异步发送 MQ 的方式进行名片识别。
将刚插入数据库的数据 ID 作为消息体发送,可使用 ActiveMQ、Rocket MQ、Alimns等消息队列处理。
3、接收队列、进行识别
在服务端新建一个 Service 进行队列接收,并根据消息内容从第二步的数据表中获取上传文件的 OSS 地址,将该压缩包下载解压后,依次识别每个名片信息。识别成功后可将识别的数据插入到目标数据表,并同时通过企业微信、钉钉、短信等通知上传用户。
名片的识别可使用阿里云 视觉智能开放平台>文字识别>名片识别 或者 其他名片识别。此处以 Laravel 框架展示主要代码内容:
/*** 处理名片信息 *@param 压缩文件的下载地址 */$fileUrl){ (//下载文件 $filePath= ('downloads/mycards.zip'); $downloadFile= ("compress.zlib://".$fileUrl); $filePath, $downloadFile); (//解压文件 "unzip -d ". ("downloads/mycards/").' -o '.$filePath); (//循环文件 $allFiles= \:: ('download')-> ("mycards/"); foreach ($allFiles$curFile) { $curImagePath= ("downloads/".$curFile); $curImage= ( ($curImagePath)); $ret=$this-> ($curImage); if (! ($ret)) { $curContent= [ "name"=>$ret["name"], "company"=>$ret["company"], "department"=>$ret["department"], "title"=>$ret["title"], "tel_cell"=>$ret["tel_cell"], "tel_work"=>$ret["tel_work"], "addr"=>$ret["addr"], "email"=>$ret["email"] ]; "db")-> ("mysql")-> ("tbcontact")-> ($curContent); ( } //删除文件 unlink($curImagePath); } //发送消息提醒 #todo//删除下载文件 unlink($filePath); } /*** 识别名片 *@param 图片 或者图片*/$imageInfo){ ($url="https://bizcard.market.alicloudapi.com/rest/160601/ocr/ocr_business_card.json"; $method="POST"; $appcode="your app code"; $headers= [ "Authorization:APPCODE ".$appcode, "Content-Type".":"."application/json; charset=UTF-8" ]; //图片二进制数据的 编码或者图片$bodys="{\"image\":\"".$imageInfo."\"}"; $ret=""; { $curl= (); $curl, , $method); ($curl, , $url); ($curl, , $headers); ($curl, , ); ($curl, , ); ($curl, , ); ($curl, , ); ($curl, , ); ($curl, , $bodys); ($ret= ($curl); $curl); ( } $ex){ (\ } $retArr= ($ret, ); if ( ($retArr)) { return []; } if (!$retArr["success"]) { return []; } return$retArr; }