最近一个微信端项目的功能涉及一个手机拍照上传身份证识别身份证信息回显的功能,调用的接口是腾讯云OCR的卡证识别功能。看了腾讯云的api通俗易懂,本地写好了demo测试可用。H5界面手机拍照调用后台方法保存身份证照片进行识别也可用,难度就在于后台Controller需要再去调用一层接口,在接口实现图片的下载,返回图片下载的物理路径和真实路径。由于后台Controller接收页面的文件类型为MultipartFile类型,亲测使用了三种方法进行传输。只有第三种在测试和线上是可以的。
第一种是将MultipartFile类型转成Base64编码,将Base64编码放进map进行http的dopost传输到接口,接口接收Base64编码,将Base64编码再转成MultipartFile进行下载。以下代码为相关关键代码,具体完整代码例子在第三种方法列出。后台Controller方法:
/*** 下载图片到接口* @return*/
@RequestMapping(value = "/ocrImgUpload", method = RequestMethod.POST,produces = "text/html;charset=utf-8")
@ResponseBody
public String ocrImgUpload(HttpServletRequest request, HttpServletResponse response, @RequestParam("file") MultipartFile mfile){try {logger.info(new Date() + "=============================下载身份证图片================================");BASE64Encoder bEncoder = new BASE64Encoder();String[] suffixArra= mfile.getOriginalFilename().split("\\.");String preffix="data:image/jpg;base64,".replace("jpg", suffixArra[suffixArra.length - 1]);String base64EncoderImg = preffix + bEncoder.encode(mfile.getBytes()).replaceAll("[\\s*\t\n\r]", "");Map
}
接口方法:
/*** 接口接收图片* @return*/
@RequestMapping(value = "/ocrImgUpload", method = RequestMethod.POST,produces = "text/html;charset=utf-8")
@ResponseBody
public void ocrImgUpload(HttpServletRequest request, HttpServletResponse response){Long begintime = (new Date()).getTime();log.info("--------------ocrImgUpload------"+ "begintime========================"+begintime);PrintWriter out = null;String res = "";try{response.setContentType("text/html; charset=" + jsonCode);out = response.getWriter();String base64EncoderImg = request.getParameter("base64EncoderImg")==null?"":request.getParameter("base64EncoderImg").trim();MultipartFile multipartFile = BASE64DecodedMultipartFile.base64ToMultipart(base64EncoderImg);Map
}
第二种是将MultipartFile类型转成File类型,将File类型放进Json传输,接口接收File类型文件,再将File类型转成MultipartFile类型进行下载,以下代码为相关关键代码,具体完整代码例子在第三种方法列出。
/*** 后台web端Controller* @return*/
@RequestMapping(value = "/ocrImgUpload", method = RequestMethod.POST,produces = "text/html;charset=utf-8")
@ResponseBody
public String ocrImgUpload(HttpServletRequest request, HttpServletResponse response, @RequestParam("file") MultipartFile mfile){try {logger.info("=============================下载身份证图片================================");File file = new File(mfile.getOriginalFilename());FileUtils.copyInputStreamToFile(mfile.getInputStream(),file);String multfile = JSON.toJSONString(file);Map
}/*** 接口方法* @return*/
@RequestMapping(value = "/ocrImgUpload", method = RequestMethod.POST,produces = "text/html;charset=utf-8")
@ResponseBody
public void ocrImgUpload(HttpServletRequest request, HttpServletResponse response){Long begintime = (new Date()).getTime();log.info("--------------ocrImgUpload------"+ "begintime========================"+begintime);PrintWriter out = null;String res = "";try{response.setContentType("text/html; charset=" + jsonCode);out = response.getWriter();String multfile = request.getParameter("multfile")==null?"":request.getParameter("multfile").trim();String file = StringEscapeUtils.unescapeHtml(multfile);File newFile = JSONObject.parseObject(file, File.class);FileInputStream input = new FileInputStream(newFile);MultipartFile multipartFile = new MockMultipartFile("file", newFile.getName(), "text/plain", IOUtils.toByteArray(input));Map
}
以上两种方法虽然都能成功下载身份证,但是在测试环境和线上环境却报图片不存在,因为这两种方法在将图片类型进行转换的时候,使用FileInputStream流输出,并且在本地项目下生成了新图片,但是此时我还未调用到图片下载的方法,即未将图片下载到服务器上,我本地使用的是tomcat。所以造成了图片未找到的报错。由于我是本地环境所以不存在图片未找到。以下给出项目完整代码。项目需要使用到的依赖:
前端H5界面代码(以下代码给出识别正面身份证代码,反面同正面一样,只需要将FRONT改为BACK即可,此处可查看腾讯云OCR识别的API参数传参):
人像面 国徽面//设置隐藏域接收图片地址
//拍照按钮
function frontclick() {$('#front_z').click();
}//反面
function backclick() {if($("#filePath1").val() == null || $("#filePath1").val() == "" || $("#filePath1").val() == undefined){layer.msg("请先拍照身份证人像面",{time:1000});return;}$('#back_z').click();
}//这里使用layui上传组件,引入layui的js和css即可//上传完毕后调用frontOcr()方法,传入图片物理路径调用后台方法进行识别回显
//正面调用腾讯云OCR认证
function frontOcr(imageStr, idCardSide) {$.ajax({url: pcs.common.path + "/web/ocr/getIdcardOcr",data: {imageStr: imageStr,idCardSide: idCardSide},type: 'post',dataType: 'json',success: function (data) {if (data.success) {var cardInfo = data.rs;var Address = cardInfo.Address;var Birth = cardInfo.Birth;var IdNum = cardInfo.IdNum;var Name = cardInfo.Name;var Nation = cardInfo.Nation;var Sex = cardInfo.Sex;var tempId = cardInfo.AdvancedInfo;var obj = JSON.parse(tempId);var arr = obj.WarnInfos;//以下值都为页面表单字段 自行添加即可$("#Name").val(Name);$("#Sex").val(Sex);$("#IdNum").val(IdNum);$("#Address").val(Address);$("#tempId").val(arr);$("#Birth").val(Birth);} else {layer.msg(data.errmsg,{time:1000});}}});
}
这行代码是设置手机拍照功能。
WebController:
/*** WebController层* @return*/
@RequestMapping(value = "/ocrImgUpload", method = RequestMethod.POST,produces = "text/html;charset=utf-8")
@ResponseBody
public String ocrImgUpload(HttpServletRequest request, HttpServletResponse response, @RequestParam("file") MultipartFile mfile){try {logger.info(new Date() + "=============================下载身份证图片================================");MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;MultipartFile file = multipartRequest.getFile("file");//从request中获取前端穿过来的文件//调用httpclientString res = HttpCilentMultipartFile.uploadFile(CommonUtils.getRandomAddr(itfUrl)+"/web/file/ocrImgUpload", file, "file");Map resMap = WebUtils.getResMap(res);String rs = (String)resMap.get("rs");return WebUtils.resultData("1", "上传成功", JSONObject.parse(rs));}catch (Exception e){logger.error("TencentApiOcrController.java-ocrImgUpload-Exception: ", e);return WebUtils.resultData("-9999","上传失败",null);}
}
HTTP传输工具类:
package com.fjqwkj.commons.utils;import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;/*** @Author: fd* @Date 2020/9/11 10:13* @Description:*/
public class HttpCilentMultipartFile {public static String uploadFile(String requestURL , MultipartFile file, String fileParamName) {CloseableHttpClient httpClient = HttpClients.createDefault();String result = "";try {String fileName = file.getOriginalFilename();HttpPost httpPost = new HttpPost(requestURL);//添加header
// for (Map.Entry
// httpPost.addHeader(e.getKey(), e.getValue());
// }MultipartEntityBuilder builder = MultipartEntityBuilder.create();builder.setCharset(Charset.forName("utf-8"));builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);//加上此行代码解决返回中文乱码问题builder.addBinaryBody(fileParamName, file.getInputStream(), ContentType.MULTIPART_FORM_DATA, fileName);// 文件流/*for (Map.Entry
接口itfController:
/*** 下载图片到接口* @return*/@RequestMapping(value = "/ocrImgUpload", method = RequestMethod.POST)public void ocrImgUpload(HttpServletRequest request, HttpServletResponse response,@RequestParam(value = "file") MultipartFile file){Long begintime = (new Date()).getTime();log.info("--------------ocrImgUpload------"+ "begintime========================"+begintime);PrintWriter out = null;String res = "";try{response.setContentType("text/html; charset=" + jsonCode);out = response.getWriter();
// String base64EncoderImg = request.getParameter("base64EncoderImg")==null?"":request.getParameter("base64EncoderImg").trim();
// MultipartFile multipartFile = BASE64DecodedMultipartFile.base64ToMultipart(base64EncoderImg);Map
public Map
本地项目文件路径配置:
#####文件路径配置
local_url: http://127.0.0.1:8080/file/upload/tem_file/ #临时访问路径
tmp_url: E:/tomcat/apache-tomcat-7.0.94/webapps/file/upload/tem_file/ #临时存放路径
real_path: E:/tomcat/apache-tomcat-7.0.94/webapps/file/upload/real_file/ #真实路径
real_url: http://127.0.0.1:8080/file/upload/real_file/
到此为止才将文件从h5传到webcontroller再http传输到接口进行下载。最后一步就是将返回的图片进行OCR识别回显信息。
/*** 获取身份证OCR识别信息.* @return*/@RequestMapping(value = "/getIdcardOcr",method = RequestMethod.POST,produces = "text/html;charset=utf-8")@ResponseBodypublic String getIdcardOcr(HttpServletRequest request){try {String imageStr = request.getParameter("imageStr")==null?"":request.getParameter("imageStr").trim();String idCardSide = request.getParameter("idCardSide")==null?"":request.getParameter("idCardSide").trim();imageStr = StrUtils.xssEncode(imageStr);idCardSide = StrUtils.xssEncode(idCardSide);Map
public interface TencentApiOcrService {public String getIdcardOcr(String cardSide, String imageStr);}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fjqwkj.commons.utils.CommonUtils;
import com.fjqwkj.commons.utils.HttpRequestProxy;
import com.fjqwkj.commons.utils.WebUtils;
import com.fjqwkj.service.apiocr.TencentApiOcrService;
import com.fjqwkj.service.checkocr.TCheckOcrService;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.ocr.v20181119.OcrClient;
import com.tencentcloudapi.ocr.v20181119.models.IDCardOCRRequest;
import com.tencentcloudapi.ocr.v20181119.models.IDCardOCRResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;/*** @Author: fd* @Date 2020/9/2 20:48* @Description:*/
@Service
public class TencentApiOcrServiceImpl implements TencentApiOcrService {private static Logger logger = LoggerFactory.getLogger(TencentApiOcrService.class);private static final String FIELD_SECRET_ID = "";//填写secretIdprivate static final String FIELD_SECRET_KEY = "";//填写secretKeyprivate static final String FIELD_TENCENT_CLOUD_API = "ocr.tencentcloudapi.com";private static final String FIELD_REGION = "ap-guangzhou";@Value("${itf_url:}")private String itfUrl;@Autowiredprivate TCheckOcrService tCheckOcrService;/*** 获取OcrClient.** @return*/private OcrClient getOcrClient(){Credential cred = new Credential(FIELD_SECRET_ID, FIELD_SECRET_KEY);HttpProfile httpProfile = new HttpProfile();httpProfile.setEndpoint(FIELD_TENCENT_CLOUD_API);ClientProfile clientProfile = new ClientProfile();clientProfile.setHttpProfile(httpProfile);OcrClient ocrClient = new OcrClient(cred, FIELD_REGION, clientProfile);return ocrClient;}/*** 身份证识别** @param cardSide* @param imageStr* @return*/public String getIdcardOcr(String cardSide, String imageStr) {OcrClient ocrClient = getOcrClient();Map
到这里整个流程就结束,可以实现身份证识别回显。总结一下,如果使用base64传输的话,因为将图片编码成base64位,编码大小会随着图片的大小像素而改变,将普通拍照上传的一张图片进行编码后,查看它的字符多达600多万,而http传输过程String类型有大小限制,会导致接口那边接收到的base64编码是空的,导致无法转成图片进行下载。还有使用base64编码的一个坏处就是容易造成解码错误,与原来图片不匹配。最主要还是生成的base64编码实在太大,过程太慢了。所以最后使用MultipartFile传输是最快最安全的。