该项目是基于springboot实现的,通过上传身份证真反面照片可以显示出身份证的详细信息
官方文档中提供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=100MB
file.upload.path=存储图片本地路径
aliapi.accessKeyId=xxx
aliapi.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/[email protected]/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:控制层
@Controller
public class MainController {
@Value("${file.uplaod.path}")
private String uploadDir;
@Autowired
private 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")
@ResponseBody
public 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接口服务层
@Service
public class OcrService {
private Client orcClient;
private RuntimeOptions runtimeOptions;
@Value("${aliapi.accessKeyId}")
private String accessKeyId;
@Value("${aliapi.accessKeySecret}")
private String accessKeySecret;
@PostConstruct
private 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>>(){});
}
}