excel解析图片pdf附件不怕

背景

工作中肯定会有导入excel还附带图片附件的下面是我解析的excel,支持图片、pdf、压缩文件

实现

依次去解析excel,看看也没有附件,返回的格式是Map,key是第几行,value是附件list附件格式都被解析成pdf格式

Reader.java


package com.ruoyi.srm.service;import java.util.List;import org.apache.poi.ss.usermodel.Workbook;import com.ruoyi.srm.domain.req.CapacityReceivingReq.FileListBean;public interface Reader {/*** @param workbook* @param targetRow 目标行索引(例如第3行,索引从0开始)* @return*/List<FileListBean> read(Workbook workbook, int targetCol);}

ReaderComposite.java


package com.ruoyi.srm.service.impl;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import com.ruoyi.srm.domain.req.CapacityReceivingReq.FileListBean;
import com.ruoyi.srm.service.Reader;@Component
public class ReaderComposite {@Autowiredprivate List<Reader> readerList;/*** @param workbook* @param targetRow 目标行索引(例如第3行,索引从0开始)* @return*/public Map<String, List<FileListBean>> read(Workbook workbook, int targetCol) {return readerList.stream().map(reader -> reader.read(workbook, targetCol)).flatMap(Collection::stream).collect(Collectors.groupingBy(t -> t.getLine() + ""));}}

ImageReader.java


package com.ruoyi.srm.service.impl;import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFPicture;
import org.apache.poi.xssf.usermodel.XSSFPictureData;
import org.apache.poi.xssf.usermodel.XSSFShape;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.stereotype.Component;import com.ruoyi.srm.domain.req.CapacityReceivingReq.FileListBean;
import com.ruoyi.srm.service.Reader;import cn.hutool.core.io.FileUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;@Slf4j
@Component
public class ImageReader implements Reader {/*** @param workbook* @param targetRow 目标行索引(例如第3行,索引从0开始)* @return*/@Override@SneakyThrowspublic List<FileListBean> read(Workbook workbook, int targetCol) {ApplicationHome home = new ApplicationHome();String rootPath = home.getDir().getAbsolutePath() + File.separator + "extract" + File.separator;List<FileListBean> result = new ArrayList<>();Map<String, AtomicInteger> counter = new HashMap<>();// 指定要读取图片的工作表和单元格位置Sheet sheet = workbook.getSheetAt(0); // 第一个工作表// 遍历所有绘图对象(包含图片)if (sheet instanceof XSSFSheet) {XSSFSheet xssfSheet = (XSSFSheet) sheet;XSSFDrawing drawing = xssfSheet.getDrawingPatriarch();if (drawing != null) {// 遍历所有形状(包括图片)String dir = rootPath + "_" + System.currentTimeMillis();for (XSSFShape shape : drawing.getShapes()) {if (shape instanceof XSSFPicture) {XSSFPicture picture = (XSSFPicture) shape;XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor();// 检查图片的左上角是否在目标单元格int targetRow = anchor.getRow1();if (anchor.getCol1() == targetCol) {AtomicInteger integer = counter.computeIfAbsent(targetRow + "_" + targetCol, k -> new AtomicInteger());// 提取图片数据XSSFPictureData pictureData = picture.getPictureData();byte[] imageBytes = pictureData.getData();// 保存图片到本地new File(dir).mkdirs();String filePath = dir + File.separator + "image_" + (targetRow + 1) + "_" + targetCol + "_" + integer.incrementAndGet() + "." + pictureData.suggestFileExtension();try (FileOutputStream out = new FileOutputStream(filePath)) {out.write(imageBytes);log.info("第{}行图片已保存到: {}", targetRow + 1, filePath);String encodeToString = Base64.getEncoder().encodeToString(FileUtil.readBytes(filePath));String mimeType = FileUtil.getMimeType(filePath);if ("image/jpeg".equals(mimeType)) {encodeToString = "data:image/png;base64," + encodeToString;}result.add(new FileListBean().setFileName(new File(filePath).getName()).setContent(encodeToString).setLine(targetRow));}}}}}}return result;}
}

AttachmentReader.java


package com.ruoyi.srm.service.impl;import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFObjectData;
import org.apache.poi.xssf.usermodel.XSSFShape;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.tika.Tika;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.stereotype.Component;import com.ruoyi.srm.domain.req.CapacityReceivingReq.FileListBean;
import com.ruoyi.srm.service.Reader;import cn.hutool.core.io.FileUtil;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;@Slf4j
@Component
public class AttachmentReader implements Reader {/*** @param workbook* @param targetRow 目标行索引(例如第3行,索引从0开始)* @return*/@Override@SneakyThrowspublic List<FileListBean> read(Workbook workbook, int targetCol) {ApplicationHome home = new ApplicationHome();String rootPath = home.getDir().getAbsolutePath() + File.separator + "extract" + File.separator;List<FileListBean> result = new ArrayList<>();Map<String, AtomicInteger> counter = new HashMap<>();// 1. 获取所有嵌入对象Sheet sheet = workbook.getSheetAt(0); // 第一个工作表// 1. 获取所有嵌入对象XSSFSheet xssfSheet = (XSSFSheet) sheet;List<POIXMLDocumentPart> relationList = xssfSheet.getRelations();// 在遍历嵌入对象时,检查锚点位置for (POIXMLDocumentPart part : relationList) {if (part instanceof XSSFDrawing) {XSSFDrawing drawing = (XSSFDrawing) part;for (XSSFShape shape : drawing.getShapes()) {if (shape instanceof XSSFObjectData) {XSSFObjectData objData = (XSSFObjectData) shape;XSSFClientAnchor anchor = (XSSFClientAnchor) objData.getAnchor();// 检查锚点是否在目标位置(例如第3行第2列,即B3)int targetRow = anchor.getRow1(); // 行索引从0开始if (anchor.getCol1() == targetCol) {AtomicInteger integer = counter.computeIfAbsent(targetRow + "_" + targetCol, k -> new AtomicInteger());// 提取并保存文件byte[] objectData = objData.getObjectData();@CleanupPOIFSFileSystem poifs = new POIFSFileSystem(new ByteArrayInputStream(objectData));String symbol = "\u0001Ole10Native";if (poifs.getRoot().getEntryNames().contains(symbol)) {InputStream contentStream = poifs.createDocumentInputStream(symbol);String dir = rootPath + "_" + System.currentTimeMillis();new File(dir).mkdirs();String name = "";byte[] byteArray = IOUtils.toByteArray(contentStream);Tika tika = new Tika();String detect = tika.detect(byteArray);System.err.println(detect);if ("application/pdf".equals(detect)) {name = dir + File.separator + "pdf_" + (targetRow + 1) + "_" + targetCol + "_" + integer.incrementAndGet() + ".pdf";} else if ("application/octet-stream".equals(detect)) {
//                                    name = dir + ".zip"; 注释
//                                    @Cleanup
//                                    ZipArchiveInputStream seek = new ZipArchiveInputStream(new ByteArrayInputStream(byteArray));
//                                    try {
//                                        seek.getNextEntry();
//                                    } catch (Exception e) {
//                                        log.debug("解析zip失败.尝试解析成图片");
//                                        name = dir + File.separator + "image_" + (targetRow + 1) + "_" + targetCol + "_" + integer.incrementAndGet() + ".jpg";
//                                    }}@CleanupFileOutputStream out = new FileOutputStream(name);out.write(byteArray);log.info("第{}行{}文件保存成功: {}", targetRow + 1, detect, name);if (name.endsWith(".zip")) {@CleanupZipArchiveInputStream zis = new ZipArchiveInputStream(new FileInputStream(name));ZipArchiveEntry entry;while ((entry = zis.getNextEntry()) != null) {if (entry.isDirectory()) {log.warn("是目录");} else {// 如果是文件,则解压文件File file = new File(dir, entry.getName());try (FileOutputStream out2 = new FileOutputStream(file)) {byte[] buffer2 = new byte[1024];int len;while ((len = zis.read(buffer2)) > 0) {out2.write(buffer2, 0, len);}}log.info("第{}提取{}已保存到: {}", targetRow + 1, entry.getName(), file.getAbsolutePath());}}}// 转base64Arrays.stream(FileUtil.ls(dir)).forEach(item -> {// System.err.println(item.getName());extracted(result, targetRow, item);});}}}}}}return result;}private static void extracted(List<FileListBean> result, int targetRow, File item) {String path = item.getPath();String encodeToString = Base64.getEncoder().encodeToString(FileUtil.readBytes(path));String mimeType = FileUtil.getMimeType(path);// System.err.println(mimeType);if ("image/jpeg".equals(mimeType)) {encodeToString = "data:image/png;base64," + encodeToString;} else {// System.err.println(encodeToString);}result.add(new FileListBean().setFileName(item.getName()).setContent(encodeToString).setLine(targetRow));}
}

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

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

相关文章

python爬虫 线程,进程,协程

0x00 线程 线程是一个执行单位&#xff0c;是在一个进程里面的&#xff0c;是共享进程里面的提供的内存等资源&#xff0c;使用多个线程时和使用多个进程相比&#xff0c;多个线程使用的内存等资源较少。进程像一座“房子”&#xff08;独立资源&#xff09;&#xff0c;线程是…

ES|QL,知道吗,专为搜索而生 —— 推出评分和语义搜索

作者&#xff1a;来自 Elastic Ioana Tagirta 在 Elasticsearch 8.18 和 9.0 中&#xff0c;ES|QL 支持评分、语义搜索以及更多的 match 函数配置选项&#xff0c;还有一个新的 KQL 函数。 使用 ES|QL 搜索 在 Elasticsearch 8.18 和 9.0 中&#xff0c;ES|QL 增加了一系列新功…

MIT6.S081-lab4

MIT6.S081-lab4 注&#xff1a;本篇lab的前置知识在《MIT6.S081-lab3前置》 1. RISC-V assembly 第一个问题 Which registers contain arguments to functions? For example, which register holds 13 in main’s call to printf? 我们先来看看main干了什么&#xff1a; …

一文总结通信电路中LC谐振回路中各公式以及对深入解读品质因数Q

目录 前言 一、基本公式总结 1.并联谐振回路 2.串联谐振回路 二、浅谈品质因数 1.衡量谐振回路能量存储与能量损耗之比的无量纲参数&#xff0c;用于描述谐振电路的频率选择性 2.当受到振荡驱动力时&#xff0c;谐振腔的中心频率与其带宽的比值 3.为什么谐振时电容上的…

Linux:文件系统

一.认识硬件–磁盘 1. 物理结构 1.2 存储结构 ❓如何定位⼀个扇区呢&#xff1f; 可以先定位磁头&#xff08;header&#xff09;——》确定磁头要访问哪⼀个柱⾯(磁道)&#xff08;cylinder&#xff09;——》 定位⼀个扇区(sector)。 柱⾯&#xff08;cylinder&#xff09…

数字孪生废气处理工艺流程

图扑数字孪生废气处理工艺流程系统。通过精准 3D 建模&#xff0c;对废气收集、预处理、净化、排放等全流程进行 1:1 数字化复刻&#xff0c;实时呈现设备运行参数、污染物浓度变化等关键数据。 借助图扑可视化界面&#xff0c;管理者可直观掌握废气处理各环节状态&#xff0c…

Scratch——第18课 列表接龙问题

在四级的考级中&#xff0c;接龙的题目虽然在CIE中只出现过两次&#xff0c;但是这类题目对字符串的知识点考察相对全面。 一、接龙游戏的判断方法 接龙的内容对应的字符数 ? 已接龙内容的字符数 满足条件>接龙内容的第一个字符数 ? 上一项接龙的最后一个字符 满足条件…

webgl入门实例-向量在图形学中的核心作用

在图形学中&#xff0c;向量是描述几何、光照、运动等核心概念的基础工具。以下是向量在图形学中的关键应用和深入解析&#xff1a; 1. 向量的核心作用 几何表示&#xff1a;描述点、方向、法线、切线等。空间变换&#xff1a;平移、旋转、缩放等操作依赖向量运算。光照计算&a…

Redis 是如何保证线程安全的?

Redis 是如何保证线程安全的&#xff1f; Redis 是一个高性能的键值数据库&#xff0c;广泛应用于缓存、消息队列、实时分析等场景。由于其性能优势&#xff0c;Redis 已经成为许多系统的核心组件之一。然而&#xff0c;很多开发者在使用 Redis 时&#xff0c;常常会问&#x…

Img2img-turbo 在2080Ti上的测试笔记

1. 介绍 [img2img-turbo]是[pytorch-CycleGAN-and-pix2pix]推荐的更新的图像变换的代码实现&#xff1b; 2. 配置信息 Conda环境名称&#xff1a;img2img-turbo 3. 问题描述 当前在我们尝试使用了官方推荐的训练命令在2080Ti上进行训练&#xff0c; 3.1 出现了 CUDA out …

代码随想录算法训练营第三十五天|416. 分割等和子集、698.划分为k个相等的子集、473.火柴拼正方形

今日题目 416. 分割等和子集 题目链接&#xff1a;416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 思考&#xff1a;本题要将数组分为两个子数组&#xff0c;且两个子数组和相等&#xff0c;因此首先可以想到的条件就是数组可分为两个&#xff0c;这要求数组元素数…

纯CSS实现自动滚动到底部

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>自动滚动到底部</title><style>*…

【新人系列】Golang 入门(十五):类型断言

✍ 个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4dd; 专栏地址&#xff1a;https://blog.csdn.net/newin2020/category_12898955.html &#x1f4e3; 专栏定位&#xff1a;为 0 基础刚入门 Golang 的小伙伴提供详细的讲解&#xff0c;也欢迎大佬们…

AI大模型发展现状与MCP协议诞生的技术演进

1. 大模型能力边界与用户痛点&#xff08;2023年&#xff09; 代表模型&#xff1a;GPT-4&#xff08;OpenAI&#xff09;、Claude 3&#xff08;Anthropic&#xff09;、通义千问&#xff08;阿里云&#xff09;等展现出强大的生成能力&#xff0c;但存在明显局限&#xff1a…

深入理解Linux中的线程控制:多线程编程的实战技巧

个人主页&#xff1a;chian-ocean 文章专栏-Linux 前言&#xff1a; POSIX线程&#xff08;Pthreads&#xff09; 是一种在 POSIX 标准下定义的线程库&#xff0c;它为多线程编程提供了统一的接口&#xff0c;主要用于 UNIX 和类 UNIX 系统&#xff08;如 Linux、MacOS 和 BS…

(mac)Grafana监控系统之监控Linux的Redis

Grafana安装-CSDN博客 普罗米修斯Prometheus监控安装&#xff08;mac&#xff09;-CSDN博客 1.Redis_exporter安装 直接下载 wget https://github.com/oliver006/redis_exporter/releases/download/v1.0.3/redis_exporter-v1.0.3.linux-amd64.tar.gz 解压 tar -xvf redis_…

鸿蒙应用元服务开发-Account Kit未成年人模式订阅和处理用户信息变更

一、概述 通过订阅用户信息变更&#xff0c;您可以接收有关用户及其账户的重要更新。当用户取消元服务的授权信息、注销华为账号时&#xff0c;华为账号服务器会发送通知到元服务&#xff0c;元服务可以根据通知消息进行自身业务处理。 二、用户信息变更事件介绍 三、订阅用…

buildroot构建根文件系统报错(已解决大部分问题)

title: buildroot构建根文件系统报错(set FORCE_UNSAFE_CONFIGURE1) author: cbus categories: 小知识 tags:小知识 abbrlink: 53691 date: 2025-04-20 08:03:00 错误1 set FORCE_UNSAFE_CONFIGURE1 在使用buildroot构建根文件系统时&#xff0c;一切按照文档的配置&#xff0…

7.QT-常用控件-QWidget|font|toolTip|focusPolicy|styleSheet(C++)

font API说明font()获取当前widget的字体信息.返回QFont对象.setFont(const QFont& font)设置当前widget的字体信息. 属性说明family字体家族.⽐如"楷体",“宋体”,"微软雅⿊"等.pointSize字体⼤⼩weight字体粗细.以数值⽅式表⽰粗细程度取值范围为[…

通过面向目标的奖励弥合人与机器人的灵活性差距

24年10月来自纽约大学的论文“Bridging the Human to Robot Dexterity Gap through Object-Oriented Rewards”。 直接通过人类视频训练机器人是机器人技术和计算机视觉领域的一个新兴领域。尽管双指机械手在双指夹持器方面取得了显著进展&#xff0c;但以这种方式让多指机械手…