百度OCR识别发票 Java 代码实现

avatar 2022年03月04日11:26:27 6 6067 views
博主分享免费Java教学视频,B站账号:Java刘哥

最近有一个需求是帮助财务人员做一个小的项目,用于员工上传发票报销,财务人员在后台进行确认和管理。

网上查了一下,感觉还是百度的做的比较好,决定使用百度 OCR

一、申请 OCR

官网地址:https://ai.baidu.com/tech/ocr

使用百度账号登录,创建应用

我这里已经创建好了一个

 

比较重要的是API KEY 和 SECRET KEY

 

二、Java代码实现

1、添加 maven 依赖

<!--百度OCR-->
<dependency>
	<groupId>com.baidu.aip</groupId>
	<artifactId>java-sdk</artifactId>
	<version>4.16.5</version>
</dependency>

 

2、创建工具类

FileUtil,Base64Util,HttpUtil,GsonUtils 工具类从下面链接下载

https://ai.baidu.com/file/658A35ABAB2D404FBF903F64D47C1F72
https://ai.baidu.com/file/C8D81F3301E24D2892968F09AE1AD6E2
https://ai.baidu.com/file/544D677F5D4E4F17B4122FBD60DB82B3
https://ai.baidu.com/file/470B3ACCA3FE43788B5A963BF0B625F3

 

3、获取 Token 类

package com.liuyanzhao.invoice.reimburse.core.util;
import com.liuyanzhao.invoice.reimburse.common.exception.LyzException;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 获取token类
 * @author liuyanzhao
 * @date 2022/3/4 9:41
 */
@Slf4j
public class AuthService {

    /**
     * 获取权限token
     * @return 返回示例:
     * {
     * "access_token": "24.460da4889caad24cccdb1fea17221975.2592000.1491995545.282335-1234567",
     * "expires_in": 2592000
     * }
     */
    public static String getAuth() {
        // 官网获取的 API Key 更新为你注册的
        String clientId = "BiEyxxxxIxxxxxxxxxxxFhigCVEd";
        // 官网获取的 Secret Key 更新为你注册的
        String clientSecret = "EmPTqbxxxxxxxxxxxxnF8gEoGE76v2E";
        return getAuth(clientId, clientSecret);
    }

    /**
     * 获取API访问token
     * 该token有一定的有效期,需要自行管理,当失效时需重新获取.
     * @param ak - 百度云官网获取的 API Key
     * @param sk - 百度云官网获取的 Secret Key
     * @return assess_token 示例:
     * "24.460da4889caad24cccdb1fea17221975.2592000.1491995545.282335-1234567"
     */
    public static String getAuth(String ak, String sk) {
        // 获取token地址
        String authHost = "https://aip.baidubce.com/oauth/2.0/token?";
        String getAccessTokenUrl = authHost
                // 1. grant_type为固定参数
                + "grant_type=client_credentials"
                // 2. 官网获取的 API Key
                + "&client_id=" + ak
                // 3. 官网获取的 Secret Key
                + "&client_secret=" + sk;
        try {
            URL realUrl = new URL(getAccessTokenUrl);
            // 打开和URL之间的连接
            HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
            connection.setRequestMethod("GET");
            connection.connect();
            // 定义 BufferedReader输入流来读取URL的响应
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String result = "";
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
            JSONObject jsonObject = new JSONObject(result);
            String access_token = jsonObject.getString("access_token");
            log.info("获取百度token成功,access_token:{}", access_token);
            return access_token;
        } catch (Exception e) {
            log.error("获取百度token失败, {}", e);
            throw new LyzwayException("获取百度OCR token失败");
        }
    }

    public static void main(String[] args)
    {
        String token = getAuth();
        System.out.println(token);
    }

}

 

4、图片/PDF 发票识别测试类

package com.liuyanzhao.invoice.reimburse.core.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.liuyanzhao.invoice.reimburse.common.constant.BaiduOcrConstant;
import com.liuyanzhao.invoice.reimburse.common.util.Base64Util;
import com.liuyanzhao.invoice.reimburse.common.util.FileUtil;
import com.liuyanzhao.invoice.reimburse.common.util.HttpUtil;

import java.net.URLEncoder;

/**
 * 增值税发票识别
 * @author liuyanzhao
 * @date 2022/3/4 8:41
 */
public class VatInvoice
{

    /**
     * 重要提示代码中所需工具类
     * FileUtil,Base64Util,HttpUtil,GsonUtils请从
     * https://ai.baidu.com/file/658A35ABAB2D404FBF903F64D47C1F72
     * https://ai.baidu.com/file/C8D81F3301E24D2892968F09AE1AD6E2
     * https://ai.baidu.com/file/544D677F5D4E4F17B4122FBD60DB82B3
     * https://ai.baidu.com/file/470B3ACCA3FE43788B5A963BF0B625F3
     * 下载
     */
    public static String vatInvoice()
    {
        // 请求url
        String url = "https://aip.baidubce.com/rest/2.0/ocr/v1/vat_invoice";
        try
        {
            // 本地文件路径
//            String filePath = "F:\\项目开发\\OCR\\发票文件\\1.png";
            String filePath = "F:\\项目开发\\OCR\\发票文件\\1.pdf";
            boolean isPdf = filePath.endsWith("pdf") || filePath.endsWith("PDF");
            byte[] fileData = FileUtil.readFileByBytes(filePath);
            String fileStr = Base64Util.encode(fileData);
            String fileParam = URLEncoder.encode(fileStr, "UTF-8");
            String param = (isPdf ? "pdf_file" : "image") + "=" + fileParam;

            // 注意这里仅为了简化编码每一次请求都去获取access_token,线上环境access_token有过期时间, 客户端可自行缓存,过期后重新获取。
            String accessToken = AuthService.getAuth();

            String result = HttpUtil.post(url, accessToken, param);
            JSONObject jsonObject = JSON.parseObject(result);
            if (jsonObject.getInteger("error_code") != null)
            {
                String errorMsg = BaiduOcrConstant.ERROR_CODE_MAP.get(jsonObject.getInteger("error_code"));
                System.out.println("票据识别失败,错误信息:"+errorMsg);
                System.out.println(jsonObject.toJSONString());
            } else
            {
                JSONObject words_result = jsonObject.getJSONObject("words_result");
                String InvoiceCode = words_result.getString("InvoiceCode"); // 发票代码
                String InvoiceNum = words_result.getString("InvoiceNum"); // 发票号码
                String InvoiceDate = words_result.getString("InvoiceDate"); // 开票日期
                String TotalTax = words_result.getString("TotalTax"); // 税额
                String TotalAmount = words_result.getString("TotalAmount"); // 金额
                String AmountInFiguers = words_result.getString("AmountInFiguers"); // 价税 小写
                String AmountInWords = words_result.getString("AmountInWords"); // 价税 大写
                String InvoiceType = words_result.getString("InvoiceType"); // 发票种类
                String InvoiceTypeOrg = words_result.getString("InvoiceTypeOrg"); // 发票名称
                System.out.println(jsonObject.toJSONString());
            }
            return result;
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args)
    {
        VatInvoice.vatInvoice();
    }
}

支持识别照片和PDF文件

 

5、错误码常量类

package com.liuyanzhao.invoice.reimburse.common.constant;

import java.util.HashMap;
import java.util.Map;

/**
 * @author liuyanzhao
 * @date 2022/3/4 10:28
 */
public class BaiduOcrConstant
{

    /**
     * 百度OCR错误码
     */
    public static final Map<Integer, String> ERROR_CODE_MAP = new HashMap<>();

    static
    {
        ERROR_CODE_MAP.put(1, "服务器内部错误,请再次请求,如果持续出现此类错误,请在控制台提交工单联系技术支持团队");
        ERROR_CODE_MAP.put(2, "服务暂不可用,请再次请求,如果持续出现此类错误,请在控制台提交工单联系技术支持团队");
        ERROR_CODE_MAP.put(3, "调用的API不存在,请检查请求URL后重新尝试,一般为URL中有非英文字符,如\"-\",可手动输入重试");
        ERROR_CODE_MAP.put(4, "集群超限额,请再次请求,如果持续出现此类错误,请在控制台提交工单联系技术支持团队");
        ERROR_CODE_MAP.put(6, "无权限访问该用户数据,创建应用时未勾选相关文字识别接口,请登录百度云控制台,找到对应的应用,编辑应用,勾选上相关接口后重新调用");
        ERROR_CODE_MAP.put(14, "IAM鉴权失败,建议用户参照文档自查生成sign的方式是否正确,或换用控制台中ak sk的方式调用");
        ERROR_CODE_MAP.put(17, "免费测试资源使用完毕,每天请求量超限额,已支持计费的接口,您可以在控制台文字识别服务选择购买相关接口的次数包或开通按量后付费;邀测和未支持计费的接口,您可以在控制台提交工单申请提升限额");
        ERROR_CODE_MAP.put(18, "QPS超限额,免费额度并发限制为2QPS,开通按量后付费或购买次数包后并发限制为10QPS,如您需要更多的并发量,可以选择购买QPS叠加包;邀测和未支持计费的接口,您可以在控制台提交工单申请提升限额");
        ERROR_CODE_MAP.put(19, "请求总量超限额,已支持计费的接口,您可以在控制台文字识别服务选择购买相关接口的次数包或开通按量后付费;邀测和未支持计费的接口,您可以在控制台提交工单申请提升限额");
        ERROR_CODE_MAP.put(100, "无效的access_token参数,token拉取失败,您可以参考“Access Token获取”文档重新获取");
        ERROR_CODE_MAP.put(110, "access_token无效,token有效期为30天,请注意需要定期更换,也可以每次请求都拉取新token");
        ERROR_CODE_MAP.put(111, "access token过期,token有效期为30天,请注意需要定期更换,也可以每次请求都拉取新token");
        ERROR_CODE_MAP.put(216100, "请求中包含非法参数,请检查后重新尝试");
        ERROR_CODE_MAP.put(216101, "缺少必须的参数,请检查参数是否有遗漏");
        ERROR_CODE_MAP.put(216102, "请求了不支持的服务,请检查调用的url");
        ERROR_CODE_MAP.put(216103, "请求中某些参数过长,请检查后重新尝试");
        ERROR_CODE_MAP.put(216110, "appid不存在,请重新核对信息是否为后台应用列表中的appid");
        ERROR_CODE_MAP.put(216200, "图片为空,请检查后重新尝试");
        ERROR_CODE_MAP.put(216201, "上传的图片格式错误,现阶段我们支持的图片格式为:PNG、JPG、JPEG、BMP,请进行转码或更换图片");
        ERROR_CODE_MAP.put(216202, "上传的图片大小错误,现阶段我们支持的图片大小为:base64编码后小于4M,分辨率不高于4096*4096,请重新上传图片");
        ERROR_CODE_MAP.put(216202, "上传的包体积过大,现阶段不支持 10M 或以上的数据包");
        ERROR_CODE_MAP.put(216630, "识别错误,请再次请求,请确保图片中包含对应卡证票据");
        ERROR_CODE_MAP.put(216631, "识别银行卡错误,出现此问题的原因一般为:您上传的图片非银行卡正面,上传了异形卡的图片、上传的银行卡正面图片不完整或模糊");
        ERROR_CODE_MAP.put(216633, "识别身份证错误,出现此问题的原因一般为:您上传了非身份证图片、上传的身份证图片不完整或模糊");
        ERROR_CODE_MAP.put(216634, "检测错误,请再次请求,如果持续出现此类错误,请在控制台提交工单联系技术支持团队");
        ERROR_CODE_MAP.put(282000, "服务器内部错误,如果您使用的是高精度接口,报这个错误码的原因可能是您上传的图片中文字过多,识别超时导致的,建议您对图片进行切割后再识别,其他情况请再次请求, 如果持续出现此类错误,请在控制台提交工单联系技术支持团队");
        ERROR_CODE_MAP.put(282003, "请求参数缺失");
        ERROR_CODE_MAP.put(282005, "处理批量任务时发生部分或全部错误,请根据具体错误码排查");
        ERROR_CODE_MAP.put(282006, "批量任务处理数量超出限制,请将任务数量减少到10或10以下");
        ERROR_CODE_MAP.put(282100, "图片压缩转码错误");
        ERROR_CODE_MAP.put(282102, "未检测到图片中识别目标,请确保图片中包含对应卡证票据,出现此问题的原因一般为:您上传了非卡证图片、图片不完整或模糊");
        ERROR_CODE_MAP.put(282103, "图片目标识别错误,请确保图片中包含对应卡证票据,出现此问题的原因一般为:您上传了非卡证图片、图片不完整或模糊");
        ERROR_CODE_MAP.put(282110, "URL参数不存在,请核对URL后再次提交");
        ERROR_CODE_MAP.put(282111, "URL格式非法,请检查url格式是否符合相应接口的入参要求");
        ERROR_CODE_MAP.put(282112, "url下载超时,请检查url对应的图床/图片无法下载或链路状况不好,或图片大小大于3M,或图片存在防盗链,您可以重新尝试以下,如果多次尝试后仍不行,建议更换图片地址");
        ERROR_CODE_MAP.put(282113, "URL返回无效参数");
        ERROR_CODE_MAP.put(282114, "URL长度超过1024字节或为0");
        ERROR_CODE_MAP.put(282808, "request id xxxxx 不存在");
        ERROR_CODE_MAP.put(282809, "返回结果请求错误(不属于excel或json)");
        ERROR_CODE_MAP.put(282810, "图像识别错误,请再次请求,如果持续出现此类错误,请在控制台提交工单联系技术支持团队");
    }
}

 

三、参考

获取accessToken接口文档地址:https://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu

增值税发票调用接口文档地址:https://ai.baidu.com/ai-doc/OCR/nk3h7xy2t

错误码地址:https://ai.baidu.com/ai-doc/OCR/dk3h7y5vr

Java语言接入地址:https://ai.baidu.com/ai-doc/OCR/Ikibizxql

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar

发表评论

avatar 登录者:匿名
匿名评论,评论回复后会有邮件通知

  

已通过评论:0   待审核评论数:0