Java实现识别发票信息

news2024/11/20 23:19:44

Java实现调用第三方接口识别发票信息

需求:对每个发票图片文件进行重命名,名称为发票号+固定信息,主要处理增值税发票

这里需要用到第三方接口,OCR识别功能,这里我用的是百度云接口,所以你需要注册百度云账号,然后生成密钥,获取权限接口!

百度云官方地址:https://console.bce.baidu.com/

百度云服务注册步骤

  • 登陆百度云账号

    扫码或手机号码短信登陆,搜索文字识别服务
    在这里插入图片描述

  • 创建应用,生成密钥

    我这里只勾选一个增值税发票识别
    请添加图片描述

  • 获取密钥

    创建完应用后直接用就行了
    请添加图片描述

代码实现步骤

  • 通过client_id和client_secret获取token
  • 通过token和相关参数请求识别发票接口
  • 返回结果
  • 处理文件
package com.shac.sellinvoiceautomation;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.ObjectUtils;

import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName OcrIdentificationInvoiceTest
 * @Description TODO
 * @Author jeckwu
 * @Date 2023/5/19 14:22
 */

@SpringBootTest
@RunWith(SpringRunner.class)
@WebAppConfiguration
@Slf4j
public class OcrIdentificationInvoiceTest {

    static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .readTimeout(15, TimeUnit.SECONDS)
            .writeTimeout(15, TimeUnit.SECONDS).build();

    // 获取token
    public String getToken() throws IOException {
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, "");
        Request request = new Request.Builder()
                .url("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=你的client_id&client_secret=你的client_secret")
                .method("POST", body)
                .addHeader("Content-Type", "application/json")
                .addHeader("Accept", "application/json")
                .build();
        Response response = HTTP_CLIENT.newCall(request).execute();
        assert response.body() != null;
        JSONObject jsonObject = JSON.parseObject(response.body().string());
        return jsonObject.getString("access_token");
    }

    // 识别发票
    public String ocrInvoice(String token,String imgBase64) throws IOException{
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        //如果Content-Type是application/x-www-form-urlencoded时,第二个参数传true
        RequestBody body = RequestBody.create(mediaType, "image="+imgBase64);
        Request request = new Request.Builder()
                .url("https://aip.baidubce.com/rest/2.0/ocr/v1/vat_invoice?access_token="+token)
                .method("POST", body)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .addHeader("Accept", "application/json")
                .build();
        Response response = HTTP_CLIENT.newCall(request).execute();
        assert response.body() != null;
        JSONObject jsonObject = JSON.parseObject(response.body().string());
        if (!ObjectUtils.isEmpty(jsonObject.getString("error_msg"))){
            log.error("识别失败,不是电子发票!msg:{}",jsonObject.getString("error_msg"));
            return null;
        }
        return jsonObject.getJSONObject("words_result").getString("InvoiceNum");
    }

    /**
     * 获取文件base64编码
     *
     * @param path      文件路径
     * @param urlEncode 如果Content-Type是application/x-www-form-urlencoded时,传true
     * @return base64编码信息,不带文件头
     * @throws IOException IO异常
     */
    static String getFileContentAsBase64(String path, boolean urlEncode) throws IOException {
        byte[] b = Files.readAllBytes(Paths.get(path));
        String base64 = Base64.getEncoder().encodeToString(b);
        if (urlEncode) {
            base64 = URLEncoder.encode(base64, "utf-8");
        }
        return base64;
    }
  
    final String invoicePath = "/Users/jeckwu/Desktop/test/IMG_8140.JPG";

    @Test
    public void test01() throws IOException {
        String token = getToken();
        String invoiceNumber = ocrInvoice(token, getFileContentAsBase64(invoicePath, true));
        if (!ObjectUtils.isEmpty(invoiceNumber))
            log.info("照片路径:{},发票号码:{}",invoicePath,invoiceNumber);
        else {
            log.info("照片路径:{},识别失败!不是电子发票!",invoicePath);
        }
    }

    final String OCR_FOLDER = "/Users/jeckwu/Desktop/test/";
    final String FILE_NAME_PREFIX = "test-invoice-";

    // 批量识别
    @Test
    public void test02() throws IOException {
        File file = new File(OCR_FOLDER);
        if (ObjectUtils.isEmpty(file.listFiles())) {
            log.info("没有要识别的发票文件!!!");
            return;
        }
        String token = getToken();
        // 遍历根目录的文件夹
        for (File listFile : Objects.requireNonNull(file.listFiles())) {
            // 是否是文件
            if (listFile.isFile()) {
                if (listFile.getAbsolutePath().indexOf(FILE_NAME_PREFIX)>0) continue;
                // 识别发票文件
                String invoiceNum= ocrInvoice(token, getFileContentAsBase64(listFile.getAbsolutePath(), true));
                if (ObjectUtils.isEmpty(invoiceNum)) {
                    log.info("照片路径:{},识别失败!不是电子发票!",listFile.getAbsolutePath());
                    continue;
                }
                // 修改文件名
                listFile.renameTo(new File(listFile.getParent() +File.separator+ FILE_NAME_PREFIX + invoiceNum + ".jpg"));
                log.info("发票路径:{},发票号码:{},识别成功",listFile.getAbsolutePath(),invoiceNum);
            }else if (listFile.isDirectory()){
                // 遍历有发票文件的文件夹
                File[] files = listFile.listFiles();
                if (ObjectUtils.isEmpty(files)) continue;
                for (File invoiceFile : files) {
                    if (invoiceFile.getAbsolutePath().indexOf(FILE_NAME_PREFIX)>0) continue;
                    // 识别发票文件
                    String invoiceNum= ocrInvoice(token, getFileContentAsBase64(invoiceFile.getAbsolutePath(), true));
                    if (ObjectUtils.isEmpty(invoiceNum)) {
                        log.info("照片路径:{},识别失败!不是电子发票!",invoiceFile.getAbsolutePath());
                        continue;
                    }
                    File tempFile = new File(invoiceFile.getParent() +File.separator+ FILE_NAME_PREFIX + invoiceNum + ".jpg");
                    // 修改文件名
//                    copyFileUsingStream(invoiceFile,tempFile);
                    invoiceFile.renameTo(tempFile);
                    log.info("发票路径:{},发票号码:{},识别成功",invoiceFile.getAbsolutePath(),invoiceNum);
                }
            }
        }
    }

    // 复制文件
    private static void copyFileUsingStream(File source, File dest) throws IOException {
        InputStream is = null;
        OutputStream os = null;
        try {
            is = new FileInputStream(source);
            os = new FileOutputStream(dest);
            byte[] buffer = new byte[1024];
            int length;
            while ((length = is.read(buffer)) > 0) {
                os.write(buffer, 0, length);
            }
        } finally {
            assert is != null;
            is.close();
            assert os != null;
            os.close();
        }
    }

}

其中百度云这个识别服务每个月有免费试用次数且有限!!有需要可以去了解详情。

识别效果

基本能识别出来,前提是发票必须清楚,识别信息也比较全面,具体可参考该接口文档。

百度云官方调试网站:https://console.bce.baidu.com/tools/#/api?product=AI&project=%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB&parent=%E8%B4%A2%E5%8A%A1%E7%A5%A8%E6%8D%AEOCR&api=rest%2F2.0%2Focr%2Fv1%2Fvat_invoice&method=post

请添加图片描述

如果文章对你有帮助请点赞支持!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/553554.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络:计网体系结构

计网体系结构 1. 基本概念1.0 计算机网络的发展1.0.1 第一阶段1.0.2 第二阶段1.0.3 第三阶段 1.1 计算机网络的概念1.2 计算机网络的功能1.3 计算机网络的组成1.4 计算机网络的分类1.5 标准化工作及相关组织1.6 相关性能指标1.6.1 速率1.6.2 带宽1.6.3 吞吐量1.6.4 时延1.6.5 时…

springboot+vue摄影跟拍预定管理系统(源码+文档)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的摄影跟拍预定管理系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 💕💕作者&#xff1…

SSM框架学习-Spring事务管理入门

文章目录 前言六、Spring事务1.Spring事务简介2.入门案例--模拟银行间转账业务3.开启Spring事务的一般步骤4.Spring事务角色5.spring事务属性--rollbackfor6.入门案例进阶--转账业务追加日志7. Spring事务属性--事务传播行为 总结 前言 为了巩固所学的知识,作者尝试…

项目管理:有效的沟通对项目的成功至关重要

为实施有效的沟通,需要建立沟通管理计划同时理解什么是沟通,沟通的对象是谁,沟通的目标是什么,难度在哪里,并选择合适的沟通方式。 项目沟通是确保项目团队的相关信息能及时、正确地产生、收集、发布、储存和最终处理…

77.建立一个Web应用程序的布局第一部分

本次我们需要设计的布局是这样样子&#xff0c;这个很想一个邮件系统的基本布局&#xff1b; ● 首先我们生成基础代码&#xff0c;基础代码很简单&#xff0c;不用过多解释 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-…

MySQL锁应用详解

文章目录 前言MySQL锁的详解1. 表级锁1.1 读锁&#xff08;共享锁&#xff09;对比查询操作更新操作获取写锁获取读锁 1.2 写锁&#xff08;排他锁&#xff09;对比获取写锁对表进行事务操作获取表的读锁对表进行查询操作 2. 行级锁2.1 共享锁2.2 排他锁 锁的应用场景1.1 并发读…

PDF怎么添加水印?简单途径说明

在工作中&#xff0c;我们经常需要对PDF文档进行保护&#xff0c;以确保其不被未经授权的人员查看或修改。其中一种常见的保护方式是在PDF文件中添加水印。水印不仅可以保护文件的安全性&#xff0c;还可以帮助识别文档的来源以及保护版权。在本文中&#xff0c;我们将介绍如何…

如何邀请媒体记者,保证新闻发布会媒体邀约?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 要邀请媒体记者参加新闻发布会并确保他们能够接受邀请&#xff0c;可以按照以下步骤进行&#xff1a; 1.制定计划&#xff1a;确定新闻发布会的日期、时间、地点和主题。确保选择一个方…

【TCP】对TCP三次握手的个人理解

三次握手 TCP 是面向连接的协议&#xff0c;所以使用 TCP 前必须先建立连接&#xff0c;而建立连接是通过三次握手来进行的。三次握手的过程如下图&#xff1a; 一开始&#xff0c;客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口&#xff0c;处于 LISTEN 状态 …

功能上新|内存篇:PSS显存、内存占用、堆内存对象快照

内存管理一直是游戏研发的重中之重&#xff0c;当项目运行时的内存压力较大时&#xff0c;更容易达到设备阈值引起闪退。近年来&#xff0c;当出海成为许多游戏公司新选择的同时&#xff0c;我们也发现海外设备对项目的内存情况有着更严格的要求。 为了帮助开发者更全面地了解…

从内核角度剖析Netty高性能的奥秘IO多路复用模型与Reactor模式

Netty 是Jboos 提供的java开源框架&#xff0c; 是基于非阻塞IO&#xff08;NIO&#xff09;的客户端/服务器编程框架&#xff0c; 它既能快速开发高并发、高可用、高可靠的网络服务器程序&#xff0c;也能开发高可用、高可靠的客户端程序。 Netty 作为异步框架&#xff0c; N…

面试:从输入URL到页面加载的全过程

首先在浏览器中输入URL 查找缓存&#xff1a;浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面&#xff0c;如果有则显示页面内容。如果没有则进行下一步。 浏览器缓存&#xff1a;浏览器会记录DNS一段时间&#xff0c;因此&#xff0c;只是第一个地方解析DNS请求…

【算法排序】动态规划

目录 一、动态规划思想二、动态规划与分治法的区别1、共同点2、不同点 三、动态规划特征1、最优子结构2、重叠子问题 四、动态规划求解问题的基本步骤五、斐波那契数分析六、实现思路七、代码实现 一、动态规划思想 将待求问题划分为若干个子问题&#xff0c;按划分的顺序求解…

为什么袁隆平的英语这么好?这才是学到老的典范!

文 / 冰雪&#xff08;微信公众号&#xff1a;王不留&#xff09; 2021年5月22日13时07分&#xff0c;“共和国勋章”获得者、中国工程院院士、国家杂交水稻工程技术研究中心主任、湖南省政协原副主席袁隆平&#xff0c;因病逝世&#xff0c;享年91岁。 一晃两年过去了。袁隆平…

iTOP-RK3568开发板编译瑞芯微原厂源码

1 输入以下命令设置 java 版本为 1.8 版本&#xff0c;确认 java 版本是 1.8 版本之后&#xff0c;才可以进行下一步编译&#xff0c;如下图所示&#xff1a; source javaenv.sh java -version 2 输入命令配置 Android 分支 source build/envsetup.sh lunch rk3568_r-user…

【软考】系统集成项目管理工程师 第3章 信息系统集成专业技术知识

文章目录 3.1 信息系统建设3.1.1 信息系统的生命周期3.1.2信息系统开发方法 3.3 软件工程3.3.1软件需求分析与定义3.3.2软件设计、测试与维护3.3.3软件质量保证及质量评价3.3.4软件配置管理3.3.5软件过程管理3.3.6软件开发工具3.3.7软件复用 3.4 面向对象系统分析与设计3.4.1面…

ESP32-WROOM-32 TCP通讯AT指令例程

ESP32-WROOM-32 AT指令配置TCP通讯 ESP32-WROOM-32前言固件烧录测试AT指令TCP通讯\透传ESP32配置SoftAPESP32作TCP Client连接TCP Server通讯/透传普通传输模式演示WIFI透传演示 ESP32做TCP Server连接TCP Client通讯演示 ESP32-WROOM-32 前言 软硬件准备&#xff1a; ESP32-W…

自动驾驶行业迎来大规模发展,“高精度定位”重要性凸显

5月16日&#xff0c;2023 中国 (亦庄) 智能网联汽车科技周暨第十届国际智能网联汽车技术年会在北京隆重召开。工信部装备工业一司一级巡视员苗长兴在在会上表示&#xff1a;2022 年我国搭载辅助自动驾驶系统的智能网联乘用车新车销售量达 700 万辆&#xff0c;同比增长45.6%&am…

计算机操作系统(慕课版)第三章课后题答案

一、简答题 1.高级调度与低级调度的主要任务是什么&#xff1f;为什么要引入中级调度&#xff1f; 1&#xff09;高级调度的主要任务是将外存的作业调入内存&#xff0c;又称作业调度&#xff1b; 低级调度的主要任务数为内存中处于就绪态的作业分配处理机。 2&#xff09;为了…

小航助学2023年3月GESP_C++二级试卷(含题库答题软件账号)

GESP在线模拟训练系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 答案:D 第1题以下存储器中的数据不会受到附近强磁场干扰的是&#xff08; &#xff09;。 A、硬盘B、U 盘C…