批量读取pdf发票中二维码的信息

news2025/1/6 13:49:30

如下代码Java类:

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.poi.ss.usermodel.BorderStyle;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * pdf电子发票二维码解析
 *
 * @author 单红宇
 * @since 2025-01-02 09:00:16
 */
@Slf4j
@Data
public class FapiaoPDFQRcodeParser {

    /**
     * folderPath
     */
    private String folderPath;
    /**
     * outputPath
     */
    private String outputPath;

    /**
     * 入口
     *
     * @param args args
     */
    public static void main(String[] args) {
        String folderPath = "D:\\Downloads\\fapiao";
        FapiaoPDFQRcodeParser fapiaoPDFQrcodeParser = new FapiaoPDFQRcodeParser();
        fapiaoPDFQrcodeParser.setFolderPath(folderPath);
        fapiaoPDFQrcodeParser.processFolder(folderPath);
    }

    /**
     * 递归处理所有pdf发票
     *
     * @param folder             folder
     * @param resultCodeDataList qrCodes
     * @param errorDataList      errorDataList
     */
    private void processFolderRecursively(File folder, List<QrCodeData> resultCodeDataList, List<ErrorData> errorDataList) {
        if (folder.isDirectory()) {
            for (File file : Objects.requireNonNull(folder.listFiles())) {
                if (file.isDirectory()) {
                    processFolderRecursively(file, resultCodeDataList, errorDataList);
                } else if (file.isFile() && file.getName().toLowerCase().endsWith(".pdf")) {
                    log.info("Processing: {}", file.getAbsolutePath());
                    String fileName = file.getAbsolutePath().substring(this.getFolderPath().length() + 1);
                    try {
                        resultCodeDataList.addAll(extractQRCodesFromPdf(file, fileName, errorDataList));
                    } catch (IOException e) {
                        errorDataList.add(new ErrorData(fileName, e.getMessage()));
                    }
                }
            }
        }
    }

    /**
     * 开始处理发票文件夹
     *
     * @param folderPath folderPath
     */
    private void processFolder(String folderPath) {
        File folder = new File(folderPath);
        List<QrCodeData> resultList = new ArrayList<>();
        List<ErrorData> errorDataList = new ArrayList<>();
        processFolderRecursively(folder, resultList, errorDataList);
        // 使用EasyExcel写入Excel文件
        // 创建ExcelWriter对象,指定文件名和文件类型(这里假设是xlsx)
        ExcelWriterBuilder writerBuilder =
                EasyExcel.write(this.getOutputPath())
                        .registerWriteHandler(this.getHorizontalCellStyleStrategy())
                        .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
        // 如果需要自定义一些全局配置,可以继续链式调用writerBuilder的其他方法
        try (ExcelWriter excelWriter = writerBuilder.build()) {// 构建ExcelWriter
            // 写入第一个sheet
            WriteSheet writeSheet1 = EasyExcel.writerSheet("Codes").head(QrCodeData.class).build();
            excelWriter.write(resultList, writeSheet1);

            // 写入第二个sheet
            WriteSheet writeSheet2 = EasyExcel.writerSheet("Errors").head(ErrorData.class).build();
            excelWriter.write(errorDataList, writeSheet2);
        } catch (Exception e) {
            log.error("输出Excel异常", e);
        }
    }

    /**
     * 设置单元格样式
     *
     * @return HorizontalCellStyleStrategy
     */
    private HorizontalCellStyleStrategy getHorizontalCellStyleStrategy() {
        // 设置边框样式
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();

        // 设置边框
        BorderStyle borderStyle = BorderStyle.THIN;
        headWriteCellStyle.setBorderTop(borderStyle);
        headWriteCellStyle.setBorderBottom(borderStyle);
        headWriteCellStyle.setBorderLeft(borderStyle);
        headWriteCellStyle.setBorderRight(borderStyle);

        contentWriteCellStyle.setBorderTop(borderStyle);
        contentWriteCellStyle.setBorderBottom(borderStyle);
        contentWriteCellStyle.setBorderLeft(borderStyle);
        contentWriteCellStyle.setBorderRight(borderStyle);

        // 创建水平样式策略
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

    /**
     * 提取pdf中的发票二维码
     *
     * @param pdfFile       pdfFile
     * @param fileName      fileName
     * @param errorDataList errorDataList
     * @return List
     * @throws IOException IOException
     */
    private List<QrCodeData> extractQRCodesFromPdf(File pdfFile, String fileName, List<ErrorData> errorDataList) throws IOException {
        List<QrCodeData> qrCodes = new ArrayList<>();
        try (PDDocument document = Loader.loadPDF(pdfFile)) {
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            for (int page = 0; page < document.getNumberOfPages(); ++page) {
                BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
                try {
                    qrCodes.addAll(decodeQRCode(bim, fileName));
                } catch (NotFoundException e) {
                    log.error("解析二维码发生异常", e);
                    errorDataList.add(new ErrorData(fileName, e.getMessage()));
                }
            }
        }
        return qrCodes;
    }

    /**
     * 解析二维码为数据对象
     *
     * @param image    image
     * @param fileName fileName
     * @return List
     */
    private List<QrCodeData> decodeQRCode(BufferedImage image, String fileName) throws NotFoundException {
        List<QrCodeData> result = new ArrayList<>();
        MultiFormatReader multiFormatReader = new MultiFormatReader();

        BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(
                new BufferedImageLuminanceSource(image)));
        Result zxingResult = multiFormatReader.decode(binaryBitmap);

        // 假设二维码内容直接包含三个值,用逗号分隔
        String[] data = zxingResult.getText().split(",");
        if (data.length > 5) {
            result.add(new FapiaoPDFQRcodeParser.QrCodeData(data[3], data[4], data[5], fileName));
        }

        return result;
    }

    /**
     * 所有发票解析后输出的excel路径
     *
     * @return String
     */
    public String getOutputPath() {
        if (outputPath == null) {
            outputPath = folderPath + File.separator + "output.xlsx";
        }
        return outputPath;
    }

    /**
     * 二维码数据对象
     *
     * @author 单红宇
     * @since 2025-01-02 09:00:16
     */
    @Data
    static class QrCodeData {
        /**
         * invoiceNumber
         */
        @ExcelProperty("发票号码")
        private String invoiceNumber;
        /**
         * amount
         */
        @ExcelProperty("金额")
        private String amount;
        /**
         * date
         */
        @ExcelProperty("日期")
        private String date;
        /**
         * fileName
         */
        @ExcelProperty("文件名")
        private String fileName;

        /**
         * QrCodeData
         *
         * @param invoiceNumber invoiceNumber
         * @param amount        amount
         * @param date          date
         * @param fileName      fileName
         */
        public QrCodeData(String invoiceNumber, String amount, String date, String fileName) {
            this.invoiceNumber = invoiceNumber;
            this.amount = amount;
            this.date = date.replace("-","").replace("/", "");
            this.fileName = fileName;
        }

    }

    /**
     * 错误数据内容
     *
     * @author 单红宇
     * @since 2025-01-02 09:00:16
     */
    @Data
    static class ErrorData {
        /**
         * fileName
         */
        @ExcelProperty("文件名")
        private String fileName;

        /**
         * errorMessage
         */
        @ExcelProperty("错误信息")
        private String errorMessage;

        public ErrorData(String fileName, String errorMessage) {
            this.errorMessage = errorMessage;
            this.fileName = fileName;
        }

    }
}

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

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

相关文章

Unity 3D柱状图效果

1.单个柱状效果展示 2.从上到下渐变透明材质Shader Shader "Unlit/NewUnlitShader" {Properties{_MainTex ("Texture", 2D) "white" {}_Color("Color",Color) (1,1,1,1)_Alpha("Alpha",Range(0,1) ) 0.2_Alpha2("…

JavaScript的数据类型及检测方式

目录 一、JS数据类型 1.基本数据类型 2.引用数据类型 二、堆和栈 三、数据类型检测 1.typeof 2.instanceof 3.constructor 4.Object.prototype.toString.call() JavaScript 中的数据类型主要分为两大类&#xff1a;原始数据类型(也称基本数据类型)和引用数据类型。 一…

电脑中缺失的nvrtc64_90.dll文件如何修复?

一、文件丢失问题 案例&#xff1a;nvrtc64_90.dll文件缺失 问题分析&#xff1a; nvrtc64_90.dll是NVIDIA CUDA Runtime Compilation库的一部分&#xff0c;通常与NVIDIA的CUDA Toolkit或相关驱动程序一起安装。如果该文件丢失&#xff0c;可能会导致基于CUDA的应用程序&…

GIT 企业级开发学习 1

本节主要命令&#xff1a; git init ls 不能列出 .git ls -a 列出 .git 1. 初始化 Git 仓库 git init • 初始化一个新的 Git 仓库&#xff0c;在当前目录下生成一个 .git 隐藏文件夹&#xff0c;用于存储版本控制信息。 2. 查看隐藏文件 ls -a • 使用 ls -a 显示隐藏文件…

用Tkinter制作一个用于合并PDF文件的小程序

需要安装PyPDF2库&#xff0c;具体原代码如下&#xff1a; # -*- coding: utf-8 -*- """ Created on Sun Dec 29 14:44:20 2024author: YBK """import PyPDF2 import os import tkinter as tk import windndpdf_files [] def dragged_files(f…

蓝桥杯JAVA--003

需求 2.代码 public class RegularExpressionMatching {public boolean isMatch(String s, String p) {if (p.isEmpty()) {return s.isEmpty();}boolean firstMatch !s.isEmpty() && (s.charAt(0) p.charAt(0) || p.charAt(0) .);if (p.length() > 2 && p…

接口开发完后,个人对于接下来接口优化的一些思考

优化点 入参的合法性和长度范围&#xff0c;必填项的检查验证 因为没有入参&#xff0c;所以不需要考虑。 批量思想解决N1问题 // 假设要查询100个订单及其对应的用户信息 List<Order> orders orderMapper.selectList(new QueryWrapper<>().last("limit …

C403 unity打开方法

1 unity hub右键以管理员方式打开。 2 注册登录账户 如果出现 如果还是不行&#xff0c;把地址栏的网址复制&#xff0c;在google浏览器中打开 如果出现安全策略&#xff0c;就不勾选安全防护 尝试方案1 把unityhub在任务管理器中关闭 如果验证码发送成功&#xff0c;还是进不…

linux-25 文件管理(三)复制、移动文件,cp,mv

命令cp是copy的简写&#xff0c;而mv则是move的简写。那既然copy是用于实现复制文件的&#xff0c;那通常一般我们要指定其要复制的是谁&#xff1f;而且复制完以后保存在什么地方&#xff0c;对吧&#xff1f;那因此它的使用格式很简单&#xff0c;那就是cp srcfile dest&…

『 Linux 』高级IO (二) - 多路转接

文章目录 前情提要新连接的获取新连接的添加不同事件的处理select 的缺点poll( )SelectServer 改为 PollServer Epoll多路转接方案Epoll 原理深入了解Epoll接口Epoll的优势 select( )/poll( )完整代码(供参考) 前情提要 在博客『 Linux 』高级IO (一)中介绍了五种IO模型; 阻塞式…

基于微信小程序的自修室预约系统

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 在知识爆炸的时代&#xff0c;自修室成为了众多学习者…

CPO-CNN-GRU-Attention、CNN-GRU-Attention、CPO-CNN-GRU、CNN-GRU四模型多变量时序预测对比

CPO-CNN-GRU-Attention、CNN-GRU-Attention、CPO-CNN-GRU、CNN-GRU四模型多变量时序预测对比 目录 CPO-CNN-GRU-Attention、CNN-GRU-Attention、CPO-CNN-GRU、CNN-GRU四模型多变量时序预测对比预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于CPO-CNN-GRU-Attention、…

ctfshow 每日练习 web 区 php特性 1-10

前置知识 这个php特性可以很好的练习我们的白盒简单代码的审计能力 web89 preg_match 正则匹配函数 &#xff08;绕过 &#xff1a; 换行符绕过 &#xff08;也可以利用他的数组返回数字进行绕过一下禁止字符的情况&#xff09;&#xff09; include("flag.php&q…

单元测试入门和mockup

Java 新手入门&#xff1a;Java单元测试利器&#xff0c;Mock详解_java mock-CSDN博客 这个是典型的before when assert三段式&#xff0c;学一下单测思路 这个没有动态代理&#xff0c;所以是直接class(对比下面) Jmockit使用笔记_增加代码覆盖率_覆盖try catch_使用new Mock…

使用Docker部署最新版JupyterHub

拉取镜像 docker pull jupyterhub/jupyterhub:latest启动镜像 docker run -d -p 8000:8000 --name jupyterhub jupyterhub/jupyterhub:latest jupyterhub进入容器 docker exec -it jupyterhub bash生成jupyterhub的配置文件 jupyterhub --generate-config# 有需要可以安装中…

MySQL 01 02 章——数据库概述与MySQL安装篇

一、数据库概述 &#xff08;1&#xff09;为什么要使用数据库 数据库可以实现持久化&#xff0c;什么是持久化&#xff1a;数据持久化意味着将内存中的数据保存到硬盘上加以“固化”持久化的主要作用是&#xff1a;将内存中的数据存储在关系型数据库中&#xff0c;当然也可以…

OLED的显示

一、I2C I2C时序&#xff1a;时钟线SCL高电平下&#xff1a;SDA由高变低代表启动信号&#xff0c;开始发送数据&#xff1b;SCL高电平时&#xff0c;数据稳定&#xff0c;数据可以被读走&#xff0c;开始进行读操作&#xff0c;SCL低电平时&#xff0c;数据发生改变&#xff1…

Java高频面试之SE-08

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本牛马baby今天又来了&#xff01;哈哈哈哈哈嗝&#x1f436; 成员变量和局部变量的区别有哪些&#xff1f; 在 Java 中&#xff0c;成员变量和局部变量是两种不同类型的变量&#xff0c;它们在作用域…

3blue1brow线代笔记

向量 物理&#xff1a;空间中的箭头&#xff0c;长度和方向决定一个向量。只要两者相同&#xff0c;可以任意移动保持不变 计算机&#xff1a;有序的数字列表 &#xff08;数组&#xff09; 数学&#xff1a;向量可以是任何东西&#xff0c;只要保证两个向量相加以及数字与向量…

开源的Vue低代码表单设计器 form-create-designer v3.2.9 版本发布,新增10多种功能

form-create-designer 是一款开源的低代码表单设计器&#xff0c;通过数据驱动表单渲染。可以通过拖拽的方式快速创建表单&#xff0c;提高开发者对表单的开发效率&#xff0c;节省开发者的时间。并广泛应用于在政务系统、OA系统、ERP系统、电商系统、流程管理等领域。 项目采…