AI 智能文本识别引擎接口手册
版本:v1.1.0
更新时间:2024-12-03 19:00:00
总体说明
“AI 智能文本识别引擎”以 HTTP 协议对外开放了调用图片的智能文本识别接口。
调用接口的过程,总体分为 3 个步骤:
(1)登录认证
(2)上传图片
(3)发起文本识别任务
在一次会话中,第(1)步登录认证只需调用一次,建立 HTTP 连接,后续的请求都可以复用同一连接,而不必每次请求都执行登录认证。
此外,所有接口的返回结果均以 JSON 格式返回,格式如下:
{
// 是否成功;
"success": true,
// 错误码;成功为 0,失败为非 0;
"code": 0,
// 错误代号;
"status": "SUCCESS",
// 与错误码、错误代号对应的错误信息;
"message": "成功",
// 接口返回的数据;
// 具体的内容,根据接口的不同而不同;
"data": ""
}
登录认证
- HTTP 请求
## 请求
Path: /app/login
Method: POST
## 头部
No-Redirect: true
Galaxy-AppId: 11113wBaTk7gy6G5Zs92BrDBixiTGpPkBzGWM
Authorization: GalaxyUser dGVzdGVyOnRzYWJjQDEyMw==;1732430549278;dWFDF8HzgRZqv52jwKqvvNEehOtc+Z2z1Z1Fxgo1N+0=
Content-Type: application/json
- HTTP 回复
{
"success": true,
"code": 0,
"status": "SUCCESS",
"message": "成功",
// data 字段结构为字符串,描述登录结果或者错误信息;
"data": "成功"
}
- Authorization 头部的编码方式:
采用了自定义的认证方案(Scheme) ,以“GalaxyUser ”为前缀, 后续接着是 username:password 的 Base64 编码,接着是“时间戳” timestamp , 最后认证的摘要信息 digest 。
GalaxyUser Base64(username:password);timestamp;SHA256(username:password:timestamp:appId:secret)
其中:
username:password :表示用户名和密码以英文半角冒号 “:” 拼接在一起的字符;登录成功后,后续的所有操作都是以该用户身份进行授权和控制;
时间戳 timestamp :是当前时间戳,表示 UTC时间 自 1970年1月1日 00:00:00.000 以来的毫秒数。
应用唯一标识 appId :在系统完成部署后,由管理员注册生成;
应用密钥 secret :在系统完成部署后,由管理员注册生成;
注意,要保护好应用密钥,不要泄露给第三方。
注意: 客户端提供的时间戳 timestamp 要尽可能保证是当前实时时间,并且与 AI 智能文件识别引擎的服务器时间偏差不超过 10 秒,超出范围的请求被认为是非法的,并有可能造成安全问题;
- 示例数据:
输入信息:
username: tester
password: tsabc@123
timestamp: 1732467004639
appId = 11113wBaTk7gy6G5Zs92BrDBixiTGpPkBzGWM
secret: RZvDhzq1RBK
生成的 Authorization 值: GalaxyUser dGVzdGVyOnRzYWJjQDEyMw==;1732467004639;N9mZpTwp1GKZRTbRhVKSZiMECUa6SlGdAyl8c8Fhna0=
上传图片
- HTTP 请求
## 请求
Path: /api/ffs/file
Method: POST
## 头部
No-Redirect: true
Content-Type: multipart/form-data;
- HTTP 回复
{
"success": true,
"code": 0,
"status": "SUCCESS",
"message": "成功",
// data 字段结构为JSON对象,描述上传成功之后返回的文件信息;
// 其中,id 字段表示文件的唯一标识,后续的识别请求都需要使用该标识;
"data": {
"id": "DY7wY7tHEmLjYPCYonpX52Lnk6RbbTXzsVCjRUbeZXsK-bcmywWzsetP.jpg",
"name": "filename.jpg",
"type": "jpg",
"size": 151051,
"contentHash": "DY7wY7tHEmLjYPCYonpX52Lnk6RbbTXzsVCjRUbeZXsK",
"createdTime": 1732461350829
}
}
发起文本识别任务
- HTTP 请求
## 请求
Path: /api/ocr/image
Method: POST
## 头部
No-Redirect: true
Content-Type: application/json;
## 请求体(Body)-JSON
{
imageIds:['AqBvA55PdhPEgdpmtzgVcDFTUhZX5BaejusM8MPPjRqV-ZMBcw61kj1v.png'],
pattern:'id-card'
}
说明: pattern 参数表示图片的解析模式,根据不同的图片类型,需要使用不同的解析模式。目前支持的解析模式请参考最后附表。
- HTTP 回复
{
"code": 0,
"success": true,
"status": "SUCCESS",
"message": "成功",
"data": {
// 任务ID;
"id": "16KuegcjmFk4u3qi",
// 识别模式;
"pattern": "id-card",
// 识别的图片ID列表;
"imageIds": [
"AqBvA55PdhPEgdpmtzgVcDFTUhZX5BaejusM8MPPjRqV-ZMBcw61kj1v.png"
],
// 识别结果;
"content": "{\n \"a\" : 123,\n \"b\" : \"cd\"\n}",
// 规则匹配结果;
"ruleMatches": [
{
"code": "rule1",
"name": "规则1",
"expression": "$data.a == 123",
// 是否通过检查;
"matched": true
},
{
"code": "rule2",
"name": "规则2",
"expression": "$data.b == 'c'",
// 是否通过检查;
"matched": false
}
]
}
}
Java 示例代码
引入依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
生成 HTTP Authorization
输入参数:
- username: 用户名;
- password: 密码;
- timestamp: 时间戳;不能与 AI 服务器的时间戳的偏差超过 10 秒;
- appId: 应用唯一标识;
- secret: 应用密钥;
String username = "tester";
String password = "tsabc@123";
String appId = "11113wBaTk7gy6G5Zs92BrDBixiTGpPkBzGWM";
String secret = "RZvDhzq1RBK";
long timestamp = 1732467004639L;
// 生成 Authorization 头部的值;
String authorization = GalaxyUserAuthenticationGenerator.generateAuthorization(username, password, timestamp, appId, secret);
System.out.println("timestamp: " + timestamp);
System.out.println("Authorization: " + authorization);
认证摘要生成算法
package com.linkgie.galaxy.ai.ocr.client;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.function.Function;
public class GalaxyUserAuthenticationGenerator {
// 基于 HTTP 协议标准头部 Authorization 进行扩展的认证方案 GalaxyUser;
public static final String HEADER = "Authorization";
// 基于 Galaxy 平台的用户名密码的认证方案;
public static final String AUTH_SCHEME = "GalaxyUser";
public static final String HEADER_VALUE_PREFIX = AUTH_SCHEME + " ";
/**
* AUTHORIZATION 认证摘要分隔符;
*/
public static final String AUTH_SECTION_SPERATOR = ";";
/**
* 信息字段分隔符;
*/
public static final String AUTH_FIELD_SPERATOR = ":";
/**
* 以当前时间戳生成认证信息;
*
* @param username 用户名;
* @param password 密码;
* @param appId 应用唯一标识;
* @param secret 应用密钥;
* @return
*/
public static String generateAuthorization(String username, String password, String appId, String secret) {
long timestamp = System.currentTimeMillis();
return generateAuthorization(username, password, timestamp, appId, secret);
}
/**
* 以指定时间戳生成认证信息;
*
* @param username 用户名;
* @param password 密码;
* @param timestamp 时间戳;当发送请求时,需要与AI服务端的时间保持一致,偏差不能超过 10 秒;
* @param appId 应用唯一标识;从控制台中以管理员登录进行查看;
* @param secret 应用密钥;从控制台中以管理员登录进行查看;注意,不要泄露给第三方;
* @return
*/
public static String generateAuthorization(String username, String password, long timestamp, String appId,
String secret) {
// 生成用户名和密码的部分;
String usernamePassword = String.join(AUTH_FIELD_SPERATOR,
username, password);
// 生成摘要;
String digestPart = generateDigestPart(usernamePassword, timestamp, appId, secret,
GalaxyUserAuthenticationGenerator::sha256);
// 生成 token;
String token = generateAuthToken(usernamePassword, timestamp, digestPart);
// 加入前缀;
String auth = HEADER_VALUE_PREFIX + token;
return auth;
}
private static String generateAuthToken(String usernamePassword, long timestamp, String digestPart) {
String usernamePasswordPart = Base64.getEncoder()
.encodeToString(toBytes_UTF8(usernamePassword));
String token = String.join(AUTH_SECTION_SPERATOR,
usernamePasswordPart, timestamp + "", digestPart);
return token;
}
/**
* 生成摘要部分;
*
* @param usernamePassword
* @param timestamp
* @param appId
* @param secret
* @param digestFunction
* @return
*/
private static String generateDigestPart(String usernamePassword, long timestamp, String appId, String secret,
Function<String, byte[]> digestFunction) {
String message = String.join(AUTH_FIELD_SPERATOR,
usernamePassword, timestamp + "", appId, secret);
byte[] digestBytes = digestFunction.apply(message);
String digestPart = Base64.getEncoder().encodeToString(digestBytes);
return digestPart;
}
/**
* 基于 JDK 的类库实现;
*
* @param message
* @return
*/
private static byte[] sha256(String message) {
// 获取SHA256算法的MessageDigest实例
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
// 使用update方法将字节数据传入进行摘要计算
digest.update(toBytes_UTF8(message));
// 通过digest方法获取最终的哈希摘要结果,返回的是字节数组
byte[] hash = digest.digest();
return hash;
}
private static byte[] toBytes_UTF8(String str) {
if (null == str) {
return null;
}
try {
byte[] bytes = str.getBytes("UTF-8");
return bytes;
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
登录
// 创建 HTTP 客户端;
HttpClient client = HttpClientBuilder.create().build();
System.out.println();
System.out.println("----------------------[ 登录 AI 服务器 ]-----------------------");
// 创建登录请求
HttpPost loginPost = new HttpPost("http://localhost:8080/app/login");
loginPost.setHeader("No-Redirect", "true");
loginPost.setHeader("Galaxy-AppId", "11113wBaTk7gy6G5Zs92BrDBixiTGpPkBzGWM");
loginPost.setHeader("Authorization", authorization);
loginPost.setHeader("Content-Type", "application/json");
// 发起调用;
HttpResponse httpResponse = client.execute(loginPost);
// 检查 HTTP 状态码;
if (httpResponse.getStatusLine().getStatusCode() != 200) {
// 发生错误;
System.err.println("调用 AI 服务失败,HTTP 响应码: " + httpResponse.getStatusLine().getStatusCode() + " - "
+ httpResponse.getStatusLine().getReasonPhrase());
throw new IllegalStateException("调用 AI 服务失败,HTTP 响应码: " + httpResponse.getStatusLine().getStatusCode()
+ " - " + httpResponse.getStatusLine().getReasonPhrase());
}
String responseText = readText(httpResponse, "UTF-8");
ServiceResponse loginResponse = deserialize(responseText, ServiceResponse.class);
System.out.println("收到的HTTP回复内容: \r\n" + responseText);
// 根据 success 字段判断是否登录成功;
if (!loginResponse.isSuccess()) {
// 登录失败;
System.err.println("登录失败,状态码:" + loginResponse.getCode() + " - " + loginResponse.getStatus()
+ " ; 错误信息: " + loginResponse.getMessage());
throw new IllegalStateException("登录失败,状态码:" + loginResponse.getCode() + " - " + loginResponse.getStatus()
+ " ; 错误信息: " + loginResponse.getMessage());
}
上传文件
System.out.println();
System.out.println("----------------------[ 开始上传文件 ]-----------------------");
// 创建上传文件的 HTTP POST 请求;
HttpPost httpPost = new HttpPost("http://localhost:8080/api/ffs/file");
File file = new File("/Users/spring/Desktop/身份证-反面-id-card-back.png");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addBinaryBody("file", file, ContentType.create("multipart/form-data", "UTF-8"), file.getName());
HttpEntity multipartEntity = builder.build();
httpPost.setEntity(multipartEntity);
// 采用已登录成功的 HttpClient 对象发起调用;
HttpResponse uploadHttpResponse = client.execute(httpPost);
String uploadResponseText = null;
if (uploadHttpResponse.getStatusLine().getStatusCode() != 200) {
// 上传文件失败,发生 HTTP 错误;
System.err.println("上传文件失败,HTTP 响应码: " + httpResponse.getStatusLine().getStatusCode() + " - "
+ httpResponse.getStatusLine().getReasonPhrase());
throw new IllegalStateException("上传文件失败,HTTP 响应码: " + httpResponse.getStatusLine().getStatusCode()
+ " - " + httpResponse.getStatusLine().getReasonPhrase());
}
uploadResponseText = readText(uploadHttpResponse.getEntity().getContent(), "UTF-8");
System.out.println("收到的HTTP回复内容: " + uploadResponseText);
ServiceResponse uploadResponse = deserialize(uploadResponseText, ServiceResponse.class);
// 根据 success 字段判断是否上传成功;
if (!uploadResponse.isSuccess()) {
// 上传文件失败;
System.err.println("上传文件失败,状态码:" + uploadResponse.getCode() + " - " + uploadResponse.getStatus()
+ " ; 错误信息: " + uploadResponse.getMessage());
throw new IllegalStateException(
"上传文件失败,状态码:" + uploadResponse.getCode() + " - " + uploadResponse.getStatus()
+ " ; 错误信息: " + uploadResponse.getMessage());
}
FileEntry uploadedFileEntry = convert(uploadResponse.getData(), FileEntry.class);
String imageId = uploadedFileEntry.getId();
System.out.println();
System.out.println("上传成功,图片Id: " + imageId);
发起图像识别任务
System.out.println();
System.out.println("----------------------[ 提交识别任务 ]-----------------------");
// 创建提交识别任务的 HTTP POST 请求;
HttpPost ocrPost = new HttpPost("http://localhost:8080/api/ocr/image");
// 以请求体(RequestBody)的方式,指定要识别图片ID列表,以及采用的识别模式;
PatternImageGroup patternImageGroup = new PatternImageGroup();
patternImageGroup.setImageIds(new String[] { imageId });
patternImageGroup.setPattern("id-card");
String json =toJson(patternImageGroup);
byte[] utf8Bytes = json.getBytes("UTF-8");
ByteArrayEntity entity = new ByteArrayEntity(utf8Bytes, ContentType.APPLICATION_JSON);
ocrPost.setEntity(entity);
// 采用已登录成功的 HttpClient 对象发起调用;
HttpResponse ocrHttpResponse = client.execute(ocrPost);
// 检查 HTTP 状态码;
if (ocrHttpResponse.getStatusLine().getStatusCode() != 200) {
// 提交识别任务失败,发生 HTTP 错误;
System.err.println("提交识别任务失败,HTTP 响应码: " + ocrHttpResponse.getStatusLine().getStatusCode() + " - "
+ ocrHttpResponse.getStatusLine().getReasonPhrase());
throw new IllegalStateException("提交识别任务失败,HTTP 响应码: " + ocrHttpResponse.getStatusLine().getStatusCode()
+ " - " + ocrHttpResponse.getStatusLine().getReasonPhrase());
}
String ocrResponseText = readText(ocrHttpResponse.getEntity().getContent(), "UTF-8");
System.out.println("收到的HTTP回复内容: " + ocrResponseText);
ServiceResponse ocrResponse = deserialize(ocrResponseText, ServiceResponse.class);
// 根据 success 字段判断是否提交识别任务成功;
if (!ocrResponse.isSuccess()) {
// 提交识别任务失败;
System.err.println("提交识别任务失败,状态码:" + ocrResponse.getCode() + " - " + ocrResponse.getStatus()
+ " ; 错误信息: " + ocrResponse.getMessage());
throw new IllegalStateException(
"提交识别任务失败,状态码:" + ocrResponse.getCode() + " - " + ocrResponse.getStatus()
+ " ; 错误信息: " + ocrResponse.getMessage());
}
// 解析识别结果;
ImageAnalyseResultVO imageAnalyseResult = convert(ocrResponse.getData(), ImageAnalyseResultVO.class);
// 识别任务Id;
imageAnalyseResult.getId();
// 识别返回的内容;
// 识别结果;
String imagesContent = imageAnalyseResult.getContent();
// 规则检测结果;
RuleMatchVO[] ruleMatches = imageAnalyseResult.getRuleMatches();
// 打印识别结果;
System.out.println("识别结果: " + imagesContent);
// 打印规则检测结果;
System.out.println("规则检测结果: ");
for (RuleMatchVO ruleMatch : ruleMatches) {
System.out.println("规则名称: " + ruleMatch.getName() + " 匹配结果: " + (ruleMatch.isMatched()? "匹配":"不匹配"));
}
附表: 标准识别模式
模式编号 | 模式名称 | 说明 |
---|---|---|
id-card | 身份证 | 身份证识别,支持双面识别 |
invoice | 增值税发票 | 发票识别,可过滤盖章、水印的影响 |
busi-license | 营业执照 | 识别内容,盖章信息 |
real-estate-reg-cert | 不动产登记证明 | 识别表格信息 |
printed-doc | 打印文稿 | 打印文字、手写文字识别 |
附表: 扩展识别模式 - 不动产登记业务
模式编号 | 模式名称 | 说明 |
---|---|---|
real-estate-reg-appform | 不动产登记申请表 | 识别表格信息,支持多页和签名识别 |
power-of-attorney | 委托授权书 | 识别打印、手写内容,签名和盖章 |
loan-clearance-cert | 贷款结清证明 | 识别打印、手写内容、签名和盖章 |
待补充更多模式