目录
一、 注册华为云账号开通识别驾驶证、行驶证服务
二、编写配置文件
2.1、配置秘钥
2.2、 编写配置工具类
三、接口测试
3.1、测试接口
3.2、结果
四、实际工作中遇到的问题
4.1、前端传值问题
4.2、后端获取数据问题
4.3、使用openfeign调用接口报错
4.3、前端显示问题
hello大家好,好久没写博客了,你们找到工作了吗?博主在去年11月成功找到工作,到现在上班大半年了,最近需求用到华为云OCR文字识别,这里就详细记录一下!
一、 注册华为云账号开通识别驾驶证、行驶证服务
华为云官网:特惠专区_云服务器_云主机_企业上云-华为云
如上图所示,华为云还有很多文字识别服务,这个看个人需求了解即可。
开放api接口地址体验:https://console.huaweicloud.com/apiexplorer/#/openapi/OCR/debug?api=RecognizeDriverLicense
二、编写配置文件
2.1、配置秘钥
ocr: projectId: xxxxxxxxxxxxxx // 项目id:华为云个人凭证获取 area: cn-north-4 // 区域:北京4区,目前好像只有北京4区支持这两个证件的识别服务 ak: xxxxxxxxxxxxx //AK:华为云个人凭证获取 sk: xxxxxxxxxxxxx //SK:华为云个人凭证获取
2.2、 编写配置工具类
@Component
public class OcrUtil {
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
@Value("${ocr.projectId}")
private String projectId;
@Value("${ocr.area}")
private String area;
@Value("${ocr.ak}")
private String AK;
@Value("${ocr.sk}")
private String SK;
private static CloseableHttpClient httpClient;
static {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(100);
cm.setDefaultMaxPerRoute(20);
cm.setDefaultMaxPerRoute(50);
httpClient = HttpClients.custom().setConnectionManager(cm).build();
}
public String getTokenByAKSK (){
String url = "https://iam."+area+".myhuaweicloud.com/v3/auth/tokens";
Map<String,String> p1 = new HashMap<>();
Map<String,Object> p2 = new HashMap<>();
Map<String,Object> p3 = new HashMap<>();
Map<String,Object> p4 = new HashMap<>();
Map<String,Object> p5 = new HashMap<>();
Map<String,Object> p6 = new HashMap<>();
Map<String,Object> p7 = new HashMap<>();
Map<String,Object> p8 = new HashMap<>();
p1.put("key",AK);
p2.put("key",SK);
p3.put("access",p1);
p3.put("secret",p2);
p4.put("hw_ak_sk",p3);
String[] str = new String[1];
str[0] = "hw_ak_sk";
p4.put("methods",str);
p5.put("identity",p4);
p6.put("name",area);
p7.put("project",p6);
p5.put("scope",p7);
p8.put("auth",p5);
String result = postJson(url, JSON.toJSONString(p8));
return result;
}
public static String postJson(String url, String jsonString) {
CloseableHttpResponse response = null;
String result = "";
try {
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
httpPost.setConfig(requestConfig);
httpPost.setConfig(requestConfig);
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setHeader("Accept", "application/json");
httpPost.setEntity(new StringEntity(jsonString, StandardCharsets.UTF_8));
response = httpClient.execute(httpPost);
Header[] h = response.getAllHeaders();
for(Header header : h){
if(header.getName().equals("X-Subject-Token")){
result = header.getValue();
}
}
} catch (IOException e) {
log.error("调用HttpUtils.Post IOException, url=" + url + ",param=" + jsonString, e);
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
log.error("调用HttpUtils.Post IOException, url=" + url + ",param=" + jsonString, e);
}
}
return result;
}
public static Result postJson(String url, String token, String jsonString) {
CloseableHttpResponse response = null;
BufferedReader in;
String result = "";
boolean err = false;
try {
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
httpPost.setConfig(requestConfig);
httpPost.setConfig(requestConfig);
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("X-Auth-Token", token);
httpPost.setEntity(new StringEntity(jsonString, StandardCharsets.UTF_8));
response = httpClient.execute(httpPost);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuilder sb = new StringBuilder();
String line;
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line).append(NL);
}
in.close();
result = sb.toString();
err = response.getStatusLine().getStatusCode() == 400;
} catch (IOException e) {
log.error("调用HttpUtils.Post IOException, url=" + url + ",param=" + jsonString, e);
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
log.error("调用HttpUtils.Post IOException, url=" + url + ",param=" + jsonString, e);
}
}
if (err) {
return Result.error().data("data", result);
} else {
return Result.ok().data("data", result);
}
}
/**
* 功能描述:
* 华为云驾驶证识别
* @Param: [token, imageUrl]
* @Return: com.ruoyi.phaseone.common.utils.R
* @Author: Mr.Huang
* @Date: 2023/8/10 10:21
**/
public Result driverLicense(String token, String imageUrl) {
String url = "https://ocr."+area+".myhuaweicloud.com/v2/"+projectId+"/ocr/driver-license";
Map<String, Object> p = new HashMap<>();
// url:图片地址 image:图片的base64数据 两个参数二选一,详情见官网API
p.put("url", imageUrl);
p.put("side", "front");
log.info("ocr驾驶证识别请求路径:{},请求参数:{}",url,p);
return postJson(url, token, JSON.toJSONString(p));
}
/**
* 功能描述:
* 华为云行驶证识别
* @Param: [token, imageUrl]
* @Return: com.sy.milkteasyservice.response.Result
* @Author: Mr.Huang
* @Date: 2023/8/14 10:52
**/
public Result vehicleLicense(String token, String imageUrl) {
String url = "https://ocr."+area+".myhuaweicloud.com/v2/"+projectId+"/ocr/vehicle-license";
Map<String, Object> p = new HashMap<>();
// url:图片地址 image:图片的base64数据 两个参数二选一,详情见官网API
p.put("url", imageUrl);
// front:行驶证主页 back:行驶证副页
p.put("side", "front");
log.info("ocr行驶证识别请求路径:{},请求参数:{}",url,p);
return postJson(url, token, JSON.toJSONString(p));
}
}
调用此接口的流程就是:获取token后设置请求头,发起请求获得结果
三、接口测试
3.1、测试接口
@RestController
public class HuaWeiObsController {
@Autowired
private ObsService ObsService;
@Autowired
private OcrUtil ocrUtil;
@ApiOperation(value = "上传图片文件",notes = "xxxxx")
@PostMapping("/uploadImgFile")
public Result upload(MultipartFile file){
String url = ObsService.upload(file);
// 行驶证识别
/* Result result = ocrUtil.vehicleLicense(ocrUtil.getTokenByAKSK(), url);
System.out.println("调用华为云行驶证识别结果:"+result.toString());
Map<String, Object> data = result.getData();
Object data1 = data.get("data");
JSONObject jsonObject = JSON.parseObject(data1.toString());
String result1 = jsonObject.get("result").toString();
System.out.println(result1);
vehicleLicense vehicleLicense = JSON.parseObject(result1, vehicleLicense.class);
System.out.println("orc识别结果是:"+vehicleLicense.toString());
return Result.ok().data("result",vehicleLicense).data("url",url);*/
// 驾驶证识别
Result result = ocrUtil.driverLicense(ocrUtil.getTokenByAKSK(), url);
System.out.println("调度接口获取的结果集:"+result);
Map<String, Object> data = result.getData();
Object data1 = data.get("data");
JSONObject jsonObject = JSON.parseObject(data1.toString());
String result1 = jsonObject.get("result").toString();
System.out.println(result1);
drivingLicence drivingLicence = JSON.parseObject(result1, drivingLicence.class);
System.out.println("orc识别结果是:"+drivingLicence.toString());
return Result.ok().data("result",drivingLicence).data("url",url);
}
}
这里的文件上传用的是华为云的OBS服务,上传图片后拿到该图片对应的地址,注意:此图片地址必须要可以访问,如设置访问权限则会调用接口失败!
3.2、结果
四、实际工作中遇到的问题
4.1、前端传值问题
问题描述:由于前端使用的是根据el-upload封装后的组件,我发现驾驶证和行驶证上传到的是同一个接口,那这样就分不清上传的驾驶证还是行驶证。
解决办法:在调用后端接口传入图片类型字段,判断是驾驶证还是行驶证
:action="this.$http.adornUrl(`/proxyKpiApi/${config.uploadUrl}?${config.id ? `id=${config.id}&` : ''}token=${$cookie.get('token')}&${config.type ? `type=${config.type}` : ''}`)"
4.2、后端获取数据问题
问题描述:项目中使用的华为云obs文件上传服务,但是上传后获得的图片地址因为安全性考虑,不能直接访问,导致调用接口失败。
// openfeign驾驶证服务
@PostMapping(value = "/performance/driverLicense", params = "{url={url}}")
R driverLicense(@RequestParam("url") String url);
// openfeign行驶证服务
@PostMapping(value = "/performance/vehicleLicense", params = "{url={url}}")
R vehicleLicense(@RequestParam("url") String url);
解决办法:将上传的文件转换成base64格式,调用接口时改成使用image参数,图片的base64数据。
if(StringUtils.isNotBlank(type)){
byte[] fileBytes = file.getBytes();
// 将上传的文件转换成base64格式
String base64String = Base64.getEncoder().encodeToString(fileBytes);
if(type.equals("driverLicense")){
// 远程调用识别驾驶证服务
com.ruoyi.phaseone.common.utils.R r = carrierPerformanceService.driverLicense(base64String);
log.info("ocr驾驶证识别请求结果:{}",r);
if(String.valueOf(r.get("code")).equals("0")){
Object data = r.get("data");
JSONObject jsonObject = JSON.parseObject(data.toString());
String result1 = jsonObject.get("result").toString();
DrivingLicenceEntity drivingLicenceEntity = JSON.parseObject(result1, DrivingLicenceEntity.class);
map.put("ocrResult",drivingLicenceEntity);
}
}else if(type.equals("vehicleLicense")){
// 远程调用识别行驶证服务
com.ruoyi.phaseone.common.utils.R r = carrierPerformanceService.vehicleLicense(base64String);
log.info("ocr行驶证识别请求结果:{}",r);
if(String.valueOf(r.get("code")).equals("0")){
Object data = r.get("data");
JSONObject jsonObject = JSON.parseObject(data.toString());
String result1 = jsonObject.get("result").toString();
VehicleLicenseEntity vehicleLicenseEntity = JSON.parseObject(result1, VehicleLicenseEntity.class);
map.put("ocrResult",vehicleLicenseEntity);
}
}
}
4.3、使用openfeign调用接口报错
问题描述:按照上面步骤修改后,由于项目是微服务架构,文件上传服务和前端调用的接口不在同一个服务上,所以要使用openfeign远程调用。但是调用的过程中报错了。报错信息:[<h1>Bad Message 414</h1><pre>reason: URI Too Long</pre>]
解决办法:此问题是因为图片数据转换成base64后,通过远程调用传的值URI太长了,因此我们将接口改造一下,将参数改成对象。
@Data
public class ImageEntity implements Serializable {
private String imageUrl;
private String imageBase64;
}
// openfeign驾驶证服务
@PostMapping( "/performance/driverLicense")
R driverLicense(@RequestBody ImageEntity image);
// openfeign行驶证服务
@PostMapping("/performance/vehicleLicense")
R vehicleLicense(@RequestBody ImageEntity image);
if(StringUtils.isNotBlank(type)){
byte[] fileBytes = file.getBytes();
// 将上传的文件转换成base64格式
String base64String = Base64.getEncoder().encodeToString(fileBytes);
ImageEntity image =new ImageEntity();
image.setImageBase64(base64String);
if(type.equals("driverLicense")){
// 远程调用识别驾驶证服务
com.ruoyi.phaseone.common.utils.R r = carrierPerformanceService.driverLicense(image);
log.info("ocr驾驶证识别请求结果:{}",r);
if(String.valueOf(r.get("code")).equals("0")){
Object data = r.get("data");
JSONObject jsonObject = JSON.parseObject(data.toString());
String result1 = jsonObject.get("result").toString();
DrivingLicenceEntity drivingLicenceEntity = JSON.parseObject(result1, DrivingLicenceEntity.class);
map.put("ocrResult",drivingLicenceEntity);
}
}else if(type.equals("vehicleLicense")){
// 远程调用识别行驶证服务
com.ruoyi.phaseone.common.utils.R r = carrierPerformanceService.vehicleLicense(image);
log.info("ocr行驶证识别请求结果:{}",r);
if(String.valueOf(r.get("code")).equals("0")){
Object data = r.get("data");
JSONObject jsonObject = JSON.parseObject(data.toString());
String result1 = jsonObject.get("result").toString();
VehicleLicenseEntity vehicleLicenseEntity = JSON.parseObject(result1, VehicleLicenseEntity.class);
map.put("ocrResult",vehicleLicenseEntity);
}
}
}
4.3、前端显示问题
问题描述:调用接口成功后,由于前端使用的是el-upload封装后的组件,用的watch函数监听值的变化,每次打开该控件都会显示其结果。
watch: {
'dataForm.carLicence.attachments' (newVal, oldVal) {
}
解决方案:判断newVal中的ocrResult是否为空,不为空在赋值,因为可以上传多个文件,所以每次更新选取集合中最后一个元素的数据
watch: {
'dataForm.carLicence.attachments' (newVal, oldVal) {
if (newVal[newVal.length - 1] && newVal[newVal.length - 1].ocrResult !== undefined && newVal[newVal.length - 1].ocrResult !== null) {
// 车牌号
this.dataForm.carNumber = newVal[newVal.length - 1].ocrResult.number;
// 发动机号
this.dataForm.engineNumber = newVal[newVal.length - 1].ocrResult.engine_no;
// 所有人
this.dataForm.carLicence.person = newVal[newVal.length - 1].ocrResult.name;
// 品牌型号
this.dataForm.carLicence.brandModel = newVal[newVal.length - 1].ocrResult.model;
// 注册日期
this.dataForm.carLicence.registerTime = newVal[newVal.length - 1].ocrResult.register_date;
// 发证日期
this.dataForm.carLicence.certificationTime = newVal[newVal.length - 1].ocrResult.issue_date;
}
}
},