excel解析图片pdf附件不怕

news2025/4/24 4:51:09

背景


	工作中肯定会有导入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 {

    @Autowired
    private 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
    @SneakyThrows
    public 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
    @SneakyThrows
    public 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();
                            @Cleanup
                            POIFSFileSystem 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";
//                                    }
                                }
                                @Cleanup
                                FileOutputStream out = new FileOutputStream(name);
                                out.write(byteArray);
                                log.info("第{}行{}文件保存成功: {}", targetRow + 1, detect, name);
                                if (name.endsWith(".zip")) {
                                    @Cleanup
                                    ZipArchiveInputStream 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());
                                        }
                                    }
                                }
                                // 转base64
                                Arrays.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.coloradmin.cn/o/2341215.html

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

相关文章

mybatis实现增删改查1

文章目录 19.MyBatis查询单行数据MapperScan 结果映射配置核心文件Results自定义映射到实体的关系 多行数据查询-完整过程插入数据配置mybatis 控制台日志 更新数据删除数据小结通过id复用结果映射模板xml处理结果映射 19.MyBatis 数据库访问 MyBatis&#xff0c;MyBatis-Plus…

Git,本地上传项目到github

一、Git的安装和下载 https://git-scm.com/ 进入官网&#xff0c;选择合适的版本下载 二、Github仓库创建 点击右上角New新建一个即可 三、本地项目上传 1、进入 要上传的项目目录&#xff0c;右键&#xff0c;选择Git Bash Here&#xff0c;进入终端Git 2、初始化临时仓库…

基于flask+vue框架的灯饰安装维修系统u49cf(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,工单人员,服务项目,订单记录,服务记录,评价记录 开题报告内容 基于 FlaskVue 框架的灯饰安装维修系统开题报告 一、选题背景与意义 &#xff08;一&#xff09;选题背景 随着城市化进程的加速与居民生活品质的显著提升&#xf…

【算法】BFS-解决FloodFill问题

目录 FloodFill问题 图像渲染 岛屿数量 岛屿的最大面积 被围绕的区域 FloodFill问题 FloodFill就是洪水灌溉的意思&#xff0c;假设有下面的一块田地&#xff0c;负数代表是凹地&#xff0c;正数代表是凸地&#xff0c;数字的大小表示凹或者凸的程度。现在下一场大雨&…

GIS开发笔记(10)基于osgearth实现二三维地图的一键指北功能

一、实现效果 二、实现原理 获取视图及地图操作器,通过地图操作器来重新设置视点,以俯仰角 (0.0)和偏航角 (-90.0)来设置。 osgEarth::Util::Viewpoint(…) 这里创建了一个新的 Viewpoint 对象,表示一个特定的视角。构造函数的参数是: 第一个参数:是视角名称。 后面的 6 个…

window上 elasticsearch v9.0 与 jmeter5.6.3版本 冲突,造成es 启动失败

[2025-04-22T11:00:22,508][ERROR][o.e.b.Elasticsearch ] [AIRUY] fatal exception while booting Elasticsearchjava.nio.file.NoSuchFileException: D:\Program Files\apache-jmeter-5.6.3\lib\logkit-2.0.jar 解决方案&#xff1a; 降低 es安装版本 &#xff0c;选择…

【C++初阶】第15课—模版进阶

文章目录 1. 模版参数2. 模版的特化2.1 概念2.2 函数模版特化2.3 类模板特化2.3.1 全特化2.3.2 偏特化 3. 模版的分离和编译4. 总结 1. 模版参数 模版参数分为类型形参和非类型参数之前我们写过的大量代码&#xff0c;都是用模版定义类的参数类型&#xff0c;跟在class和typena…

黑阈免激活版:智能管理后台,优化手机性能

在使用安卓手机的过程中&#xff0c;许多用户会遇到手机卡顿、电池续航不足等问题。这些问题通常是由于后台运行的应用程序过多&#xff0c;占用大量系统资源导致的。今天&#xff0c;我们要介绍的 黑阈免激活版&#xff0c;就是这样一款由南京简域网络科技工作室开发的手机辅助…

Mujoco robosuite 机器人模型

import ctypes import os# 获取当前脚本所在的目录 script_dir os.path.dirname(os.path.abspath(__file__))# 构建库文件的相对路径 lib_relative_path os.path.join(dynamic_models, UR5e, Jb.so)# 拼接成完整的路径 lib_path os.path.join(script_dir, lib_relative_path…

K8s:概念、特点、核心组件与简单应用

一、引言 在当今云计算和容器技术蓬勃发展的时代&#xff0c;Kubernetes&#xff08;简称 K8s&#xff09;已成为容器编排领域的事实标准。它为管理容器化应用提供了高效、可靠的解决方案&#xff0c;极大地简化了应用的部署、扩展和运维过程。无论是小型初创公司还是大型企业…

STM32的定时器输出PWM时,死区时间(DTR)如何计算

在 STM32F429&#xff08;以及所有 STM32F4 “高级定时器”&#xff09;中&#xff0c;死区时间由 TIMx_BDTR 寄存器的 8 位 “Dead‑Time Generator” 字段 DTG[7:0] 来配置。其计算分三步&#xff1a; 计算死区时钟周期 tDTS TIM1 时钟源为 APB2 定时器时钟&#xff08;PCL…

STC32G12K128单片机GPIO模式SPI操作NorFlash并实现FatFS文件系统

STC32G12K128单片机GPIO模式SPI操作NorFlash并实现FatFS文件系统 NorFlash简介NorFlash操作驱动代码文件系统测试代码 NorFlash简介 NOR Flash是一种类型的非易失性存储器&#xff0c;它允许在不移除电源的情况下保留数据。NOR Flash的名字来源于其内部结构中使用的NOR逻辑门。…

ClickHouse 设计与细节

1. 引言 ClickHouse 是一款备受欢迎的开源列式在线分析处理 (OLAP) 数据库管理系统&#xff0c;专为在海量数据集上实现高性能实时分析而设计&#xff0c;并具备极高的数据摄取速率 1。其在各种行业中得到了广泛应用&#xff0c;包括众多知名企业&#xff0c;例如超过半数的财…

智能体MCP 实现数据可视化分析

参考: 在线体验 https://www.doubao.com/chat/ 下载安装离线体验 WPS软件上的表格分析 云上创建 阿里mcp:https://developer.aliyun.com/article/1661198 (搜索加可视化) 案例 用cline 或者cherry studio实现 mcp server:excel-mcp-server、quickchart-mcp-server

再看开源多模态RAG的视觉文档(OCR-Free)检索增强生成方案-VDocRAG

前期几个工作提到&#xff0c;基于OCR的文档解析RAG的方式进行知识库问答&#xff0c;受限文档结构复杂多样&#xff0c;各个环节的解析泛化能力较差&#xff0c;无法完美的对文档进行解析。因此出现了一些基于多模态大模型的RAG方案。如下&#xff1a; 【RAG&多模态】多模…

深入浅出 NVIDIA CUDA 架构与并行计算技术

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《深度探秘&#xff1a;AI界的007》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、CUDA为何重要&#xff1a;并行计算的时代 2、NVIDIA在…

FPGA系列之DDS信号发生器设计(DE2-115开发板)

一、IP核 IP(Intellectual Property)原指知识产权、著作权等&#xff0c;在IC设计领域通常被理解为实现某种功能的设计。IP模块则是完成某种比较复杂算法或功能&#xff08;如FIR滤波器、FFT、SDRAM控制器、PCIe接口、CPU核等&#xff09;并且参数可修改的电路模块&#xff0c…

【Dv3Admin】从零搭建Git项目安装·配置·初始化

项目采用 Django 与 Vue3 技术栈构建&#xff0c;具备强大的后端扩展能力与现代前端交互体验。完整实现了权限管理、任务队列、WebSocket 通信、系统配置等功能&#xff0c;适用于构建中后台管理系统与多租户平台。 本文章内容涵盖环境搭建、虚拟环境配置、前后端部署、项目结…

P3416-图论-法1.BFS / 法2.Floyd

这道题虽然标签有floyd但是直接bfs也能过 其实事实证明还是bfs快&#xff0c;因为bfs只需要遍历特定的点&#xff0c;但是floyd需要考虑遍历所有可能的中介点 法1.BFS 用字典存储每个点所能普及的范围&#xff0c;然后用对每个点bfs进行拓展 nint(input())temp[]#xmax0;yma…

极狐GitLab 议题和史诗创建的速率限制如何设置?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 议题和史诗创建的速率限制 (BASIC SELF) 速率限制是为了控制新史诗和议题的创建速度。例如&#xff0c;如果您将限制设置为 …