Java 使用POI导入带有图片的Excel(单张/多张)

tips:该文仅记录学习,有什么不足、不成熟或者错误的地方欢迎大家指正。 

 1.需求

导入模板如下,要求将数据持久化到数据库,图片使用oss存储到minio,在查询时指定字段带出图片url。

2.使用的工具

    <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>5.2.3</version>
    </dependency>
 
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
    </dependency>

 3.想法

        通过在网上查找资料,excel对图片的定位是通过锚点定位的,poi可以通过sheet.createDrawingPatriarch();这个方法去拿到“画布”,所有的图片全在画布上。

        设计一个   Map<Integer, Map<Integer, List<Long>>>  arrangedData = new HashMap<>();
去收集图片元素,最外边一层key是图片所在的行,第二层Map的key是图片所在的列,第二层的value是图片ossId集合(一个单元格内可能会有多张图片)。

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.io.FileUtils;

import org.apache.poi.ss.usermodel.Shape;

import org.apache.poi.ss.usermodel.Sheet;

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.XSSFWorkbook;

import org.dromara.common.excel.domain.PictureData;

import org.dromara.system.domain.vo.SysOssVo;

import org.dromara.system.service.ISysOssService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import org.springframework.web.multipart.MultipartFile;



import java.io.File;

import java.io.InputStream;

import java.util.*;

import java.util.stream.Collectors;



/**

 * 带有图片的excel解析工具

 * @Author: dmz

 * @CreateTime: 2024-10-17

 */

@Component

@Slf4j

public class ExcelPictureUtil {

    @Autowired

    private ISysOssService ossService;

    /**

     * 只适用于sheet0

     * 结构为 第一层 key=行 val=该行下所有列的图片数据,第二层 key=列 val=该单元格内的所有上传图片的ossId

     * @param file

     * @return Map<Integer,Map<Integer,List<Long>>> analyzePicture

     */

    public  Map<Integer,Map<Integer,List<Long>>> analyzePicture(MultipartFile file){
        //收集结果

        Map<Integer, Map<Integer, List<Long>>>  arrangedData = new HashMap<>();

        try {
            //获取导入文件的输入流

            InputStream inputStream = file.getInputStream();
            //转工作薄对象

            XSSFWorkbook sheets = new XSSFWorkbook(inputStream);
            //获取页签(如果是多个页签可以循环获取,这里只演示一个)

            Sheet sheet = sheets.getSheetAt(0);
            //获取画布

            XSSFDrawing patriarch = (XSSFDrawing)sheet.createDrawingPatriarch();

            List<PictureData> dataList = new ArrayList<>();
            //循环遍历所有的图片数据

            for (Shape shape : patriarch.getShapes()) {

                if (shape instanceof XSSFPicture picture) {

                    XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor();

                    int row = anchor.getRow1();//行 从0开始

                    int col = anchor.getCol1();//列 从0开始

                    // 获取图片数据字节数组

                    byte[] pictureData = picture.getPictureData().getData();
                    //这里是写在本地用户目录下,再上传oss对象存储服务。可以按照自己的需求去做处理,比如存在服务器本地

                    String pathname = String.format("%s/%s.jpg", System.getProperty("user.home"), UUID.randomUUID());

                    File pictureFile = new File(pathname);

                    FileUtils.writeByteArrayToFile(pictureFile, pictureData);

                    SysOssVo sysOssVo = ossService.upload(pictureFile);

                    dataList.add(new PictureData(row,col,sysOssVo.getOssId()));
                    //这个是删除本地存储的图片,也可以使用临时目录存储,则不需要这一步

                    boolean delete = pictureFile.delete();

                    log.info("minio-ossid,{}", sysOssVo.getOssId());

                }

            }
            //将同一行列的数据进行排序收集ossId,用于和正常的数据进行结合,比如同一行的指定列为第一个数据对象的某个字段

             arrangedData = dataList.stream()

                .collect(Collectors.groupingBy(PictureData::getRow,

                    Collectors.groupingBy(PictureData::getColumn,

                        Collectors.mapping(PictureData::getOssId, Collectors.toList()))));

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

        return arrangedData;

    }

}

 PictureData 

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * excel图片导入实体类
 * @Author: dmz
 * @CreateTime: 2024-10-17
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
 public class PictureData {
    /**
     * 行
     */
    int row;
    /**
     * 列
     */
    int column;
    /**
     * ossId
     */
    long ossId;

}

 数据结合逻辑

    /**
     * 将图片数据与内容结合
     * @param file
     * @param sahPostDetailVos
     * @return
     */
    private List<SahPostInfoVo> handleExcelPicture(MultipartFile file, List<SahPostInfoVo> sahPostDetailVos) {
         //这里为上述的获取图片数据操作
        Map<Integer, Map<Integer, List<Long>>> pictureData = excelPictureUtil.analyzePicture(file);
        //遍历
        for (Integer row : pictureData.keySet()) {
            //获取图片的行号
            Map<Integer, List<Long>> integerListMap = pictureData.get(row);
            //获取图片所属的数据对象
            SahPostInfoVo sahPostInfoVo = sahPostDetailVos.get(row-1);
            for (Integer column : integerListMap.keySet()) {
                //获取图片的列号
                //进行逻辑判断
                List<Long> ossList = integerListMap.get(column);
                //根据列的索引判断该为哪个字段
                if (column.equals(PostPictureColumEnum.SUPPORTING_PAPER.getIndex()) && ossList.size() == 1) {
                    //岗位说明文件图片不超过一个
                    sahPostInfoVo.setSupportingPaper(ossList.get(0));
                }
                if (column.equals(PostPictureColumEnum.CERTIFICATE.getIndex())) {
                    String ossIds = ossList.stream()
                        .map(String::valueOf)
                        .collect(Collectors.joining(StringUtils.SEPARATOR));
                    sahPostInfoVo.setCertificate(ossIds);
                }
                if (column.equals(PostPictureColumEnum.ENVIRONMENT.getIndex())) {
                    String ossIds = ossList.stream()
                        .map(String::valueOf)
                        .collect(Collectors.joining(StringUtils.SEPARATOR));
                    sahPostInfoVo.setEnvironment(ossIds);
                }
            }
        }
        //后续可以做批量入库操作
        return sahPostDetailVos;
    }

tips:excel图片不要嵌入单元格,这样可能会导致无法从画布中获取到 

 参考:Java实现导入带有图像的 Excel 文件_java导入excel包含图片-优快云博客

### 安装 Zabbix 6.4 监控系统 #### 准备工作 为了确保顺利安装 Zabbix 6.4,在开始之前需要确认系统的软件包列表是最新的: ```bash sudo apt update && sudo apt upgrade -y ``` #### 添加 Zabbix 软件源 由于官方仓库可能不包含最新版本的 Zabbix 或特定于 Ubuntu 24.04 的版本,因此建议添加专门针对此发行版优化过的第三方镜像站。 下载并安装适用于 Ubuntu 24.04 的 Zabbix 发行版 GPG 密钥以及对应的 APT 配置文件: ```bash wget https://repo.zabbix.com/zabbix/7.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest+ubuntu24.04_all.deb dpkg -i zabbix-release_latest+ubuntu24.04_all.deb apt update ``` 注意这里使用的是 `zabbix-release` 版本来匹配目标环境的要求[^1]。 #### 解决依赖库冲突问题 如果遇到因缺少某些共享库而导致无法正常启动服务的情况,则可以通过创建符号链接的方式解决问题。对于 libtinfo.so 文件缺失的情形可以这样做: ```bash cd /usr/lib/x86_64-linux-gnu/ ln -s libtinfo.so.6.4 libtinfo.so.5 ``` 这一步骤能够有效规避部分应用程序对旧版本 NCURSES 库存在兼容性的需求[^2]。 #### 安装 MySQL 数据库服务器 考虑到数据存储的需求,还需要部署一个合适的数据库管理系统。对于大多数生产环境中推荐采用 MySQL/MariaDB 来作为后端支持。具体操作如下所示: ```bash sudo apt install mysql-server -y ``` 完成上述命令之后应当按照提示设置 root 用户密码,并执行安全脚本来进一步加强安全性配置。 #### 安装 PHP 及其扩展模块 因为 Web 前端界面需要用到 PHP 进行解析渲染,所以要提前准备好必要的解释器及相关组件: ```bash sudo apt install php-fpm php-mysql php-gd php-bcmath php-xml php-mbstring -y ``` 这些额外加载项有助于增强功能性和稳定性表现。 #### 下载并编译安装 Zabbix Server 和 Agent 组件 现在可以从官方渠道获取稳定发布的二进制包来进行本地化构建过程: ```bash sudo apt install zabbix-server-mysql zabbix-frontend-php zabbix-apache-conf zabbix-sql-scripts zabbix-agent -y ``` 以上指令会自动处理好所有必需品之间的相互关系,从而简化整个流程。 #### 初始化数据库结构与导入初始数据集 首次运行前需先建立专用的数据表空间并且填充基础记录条目进去: ```sql CREATE DATABASE zabbix CHARACTER SET utf8mb4 COLLATE utf8mb4_bin; GRANT ALL PRIVILEGES ON zabbix.* TO 'zabbix'@'localhost' IDENTIFIED BY '<password>'; FLUSH PRIVILEGES; mysql -uzabbix -p zabbix < /usr/share/doc/zabbix-sql-scripts/mysql/server.sql ``` 请记得替换 `<password>` 成实际使用的访问凭证字符串。 #### 修改默认配置文件中的连接参数设定 编辑 `/etc/zabbix/zabbix_server.conf` 文档内的 DBPassword 字段值为刚才所指定的内容;同理也要调整前端页面路径下的相同位置处的相关选项以保持一致。 #### 启动相关服务进程并将它们设为开机自启项目之一 最后一步就是激活各个子系统并验证整体运作状况良好无误: ```bash sudo systemctl restart apache2.service zabbix-server zabbix-agent sudo systemctl enable apache2.service zabbix-server zabbix-agent ``` 通过浏览器打开 http://<your_ip>/zabbix 即可进入图形化的管理控制台继续后续定制化开发之旅了!
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值