一:项目简介
该项目是基于springboot实现的,通过上传身份证真反面照片可以显示出身份证的详细信息
效果展示:
项目架构:
使用 Maven 导入SDK相关依赖
官方文档中提供2个版本的SDK,JAVA旧版需用OSS存储上传的图片,还需要单独使用OSS配置由于阿里云的是收费的,可以选择七牛云OSS对象储存来实现图片上传,我这里是选择JAVA新版本SDK,支持本地图片上传。
这里我们选择使用maven官方提供的依赖,搜索关键词ocr进行查看
<dependency><groupId>com.aliyun</groupId><artifactId>ocr</artifactId><version>1.0.3</version></dependency>
application.properties配置文件信息
spring.servlet.multipart.max-file-size=100MBfile.upload.path=存储图片本地路径aliapi.accessKeyId=xxxaliapi.accessKeySecret=xxx
二:代码结构
前端jsp静态页面:
<!DOCTYPE html><html lang=\"zh\" xmlns:th=\"http://www.thymeleaf.org\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no\"/><title>身份证识别</title><link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css\" integrity=\"sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk\" crossorigin=\"anonymous\"></head><body><div class=\"container\"><div class=\"row\"><div class=\"col-xl-12 mx-auto\"><h1>身份证识别</h1><div class=\"col-sm-12\"><p th:text=\"${messages}\" th:if=\"${messages != null}\" class=\"alert alert-info\"></p></div><form method=\"post\" th:action=\"@{/upload}\" enctype=\"multipart/form-data\"><div class=\"row\"><div class=\"col-sm-4\"><div class=\"input-group\"><input id=\"location\" class=\"form-control\" onclick=\"$(\'#i-face\').click();\"><label class=\"input-group-btn\"><input type=\"button\" id=\"i-check\" value=\"上传人像面\" class=\"btn btn-primary\" onclick=\"$(\'#i-face\').click();\"></label></div></div><input type=\"file\" name=\"face\" id=\"i-face\" accept=\".jpg, .png, .jpeg\" onchange=\"$(\'#location\').val($(\'#i-face\').val());\" style=\"display: none\"><div class=\"col-sm-4\"><div class=\"input-group\"><input id=\"locationf\" class=\"form-control\" onclick=\"$(\'#i-back\').click();\"><label class=\"input-group-btn\"><input type=\"button\" id=\"i-checkb\" value=\"上传国徽面\" class=\"btn btn-primary\" onclick=\"$(\'#i-back\').click();\"></label></div></div><input type=\"file\" name=\"back\" id=\"i-back\" accept=\".jpg, .png, .jpeg\" onchange=\"$(\'#locationf\').val($(\'#i-back\').val());\" style=\"display: none\"><div class=\"col-sm-4\"><button type=\"submit\" class=\"btn btn-primary form-control\">开始识别</button></div></div></form></div></div><div class=\"row\" style=\"margin-top: 38px;\"><div class=\"col-md-12 mx-auto\"><div class=\"row\"><div class=\"col-sm-4\"><img th:src=\"${faceImage}\" th:if=\"faceImage != null\" class=\"img-fluid\"></div><div class=\"col-sm-4\"><img th:src=\"${backImage}\" th:if=\"backImage != null\" class=\"img-fluid\"></div></div></div></div><div class=\"row\" style=\"margin-top: 38px;\"><div class=\"col-md-12 mx-auto\" th:if=\"${faceResult != null}\"><div class=\"row\"><div class=\"col-sm-4\"><p><span>姓名: </span><span th:text=\"${faceResult.name}\"></span></p><p><span>性别: </span><span th:text=\"${faceResult.gender}\"></span></p><p><span>民族: </span><span th:text=\"${faceResult.nationality}\"></span></p><p><span>出生日期: </span><span th:text=\"${faceResult.birthDate}\"></span></p><p><span>住址: </span><span th:text=\"${faceResult.address}\"></span></p><p><span>身份证号: </span><span th:text=\"${faceResult.IDNumber}\"></span></p></div><div class=\"col-sm-4\"><p><span>签发机关: </span><span th:text=\"${backResult.issue}\"></span></p><p><span>有效日期: </span><span th:text=\"${backResult.startDate}\">-<span th:text=\"${faceResult.endDate}\"></span></span></p></div></div></div></div></div></body><script src=\"https://code.jquery.com/jquery-3.5.1.slim.min.js\" integrity=\"sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj\" crossorigin=\"anonymous\"></script><script src=\"https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js\" integrity=\"sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo\" crossorigin=\"anonymous\"></script><script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js\" integrity=\"sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI\" crossorigin=\"anonymous\"></script></html>
MainController:控制层
@Controllerpublic class MainController {@Value(\"${file.uplaod.path}\")private String uploadDir;@Autowiredprivate OcrService ocrService;private List<String> faceImages = new ArrayList<>();private List<String> backImages = new ArrayList<>();private List<Map<String, String>> faceResults = new ArrayList<>();private List<Map<String, String>> backResults = new ArrayList<>();@RequestMapping(\"/index\")public String index(Model model) {if (faceImages.size() != backImages.size()) {clearAllList();}if (!CollectionUtils.isEmpty(faceImages) && faceImages.size() == backImages.size()) {model.addAttribute(\"faceImage\", faceImages.get(faceImages.size() - 1));model.addAttribute(\"faceResult\", faceResults.get(faceResults.size() - 1));model.addAttribute(\"backImage\", backImages.get(backImages.size() - 1));model.addAttribute(\"backResult\", backResults.get(backResults.size() - 1));}return \"index\";}@RequestMapping(\"/test\")@ResponseBodypublic String test() {return \"test\";}@PostMapping(\"/upload\")public String upload(@RequestParam(\"face\") MultipartFile face, @RequestParam(\"back\") MultipartFile back,RedirectAttributes redirectAttributes) {if (face.isEmpty() || back.isEmpty()) {redirectAttributes.addFlashAttribute(\"messages\", \"请选择一个文件进行上传\");return \"redirect:/index\";}String errorMessages = null;Path dir = Paths.get(uploadDir);if (!Files.exists(dir)) {try {Files.createDirectories(dir);} catch (IOException e) {e.printStackTrace();errorMessages += e.getMessage() + \"\\n\";}}try {if (!face.isEmpty()) {String filename = saveFile(face);Map<String, String> faceResult = ocrService.RecognizeIdCard(uploadDir + filename, \"face\");faceImages.add(\"/images/\" + filename);faceResults.add(faceResult);}if (!back.isEmpty()) {String filename = saveFile(back);Map<String, String> faceResult = ocrService.RecognizeIdCard(uploadDir + filename, \"back\");backImages.add(\"/images/\" + filename);backResults.add(faceResult);}} catch (Exception e) {e.printStackTrace();errorMessages += e.getMessage() + \"\\n\";}if (StringUtils.isNoneBlank(errorMessages)) {redirectAttributes.addFlashAttribute(\"messages\", errorMessages);}return \"redirect:/index\";}private void clearAllList() {faceImages.clear();backImages.clear();faceResults.clear();backResults.clear();}public String saveFile(MultipartFile file) {String filename = UUID.randomUUID().toString() + \".\"+ StringUtils.substringAfterLast(file.getOriginalFilename(), \".\");Path path = Paths.get(uploadDir + filename);try {Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);} catch (IOException e) {return null;}return filename;}}
OcrService:识别工具api接口服务层
@Servicepublic class OcrService {private Client orcClient;private RuntimeOptions runtimeOptions;@Value(\"${aliapi.accessKeyId}\")private String accessKeyId;@Value(\"${aliapi.accessKeySecret}\")private String accessKeySecret;@PostConstructprivate void init() throws Exception {Config config = new Config();config.endpointType = \"access_key\";config.regionId = \"cn-shanghai\";config.accessKeyId = accessKeyId;config.accessKeySecret = accessKeySecret;config.endpoint = \"ocr.cn-shanghai.aliyuncs.com\";orcClient = new Client(config);runtimeOptions = new RuntimeOptions();}public Map<String, String> RecognizeIdCard(String fielpath, String side) throws Exception {RecognizeIdentityCardAdvanceRequest request = new RecognizeIdentityCardAdvanceRequest();request.imageURLObject = Files.newInputStream(Paths.get(fielpath));request.side = side;RecognizeIdentityCardResponse response = orcClient.recognizeIdentityCardAdvance(request, runtimeOptions);return \"face\".equals(side) ?JSON.parseObject(JSON.toJSONString(response.data.frontResult), new TypeReference<Map<String, String>>(){}): JSON.parseObject(JSON.toJSONString(response.data.backResult), new TypeReference<Map<String, String>>(){});}}