如下代码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// 写入第一个sheetWriteSheet writeSheet1 = EasyExcel.writerSheet("Codes").head(QrCodeData.class).build();excelWriter.write(resultList, writeSheet1);// 写入第二个sheetWriteSheet 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*/@Datastatic 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*/@Datastatic class ErrorData {/*** fileName*/@ExcelProperty("文件名")private String fileName;/*** errorMessage*/@ExcelProperty("错误信息")private String errorMessage;public ErrorData(String fileName, String errorMessage) {this.errorMessage = errorMessage;this.fileName = fileName;}}
}