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贷款结清证明识别打印、手写内容、签名和盖章

待补充更多模式

最近更新:
发布者: huanghaiquan
扫码咨询