一、需求背景:

做一个大屏管理系统,基础信息包括管理的应用名称,大屏的截图,通过一个excel批量导入

excel的单元格里要插入图片,对应一个大屏应用的信息

导入需要读取到大屏截图,至于存哪还没说....

二、技术实现

Hutool这块没有做图片读取的封装,看了网上的方式都是通过poi原生的api实现的

但是Hutool读取很方便,返回的Workbook接口对象可以识别新旧工作簿类型

然后原生的api的话,只能自己去封装想要的逻辑了

几个图片的关键信息,图片字节,图片类型,图片对应的记录坐标

我还纳闷怎么拿不到图片的文件名,仔细找了API才发现是压根没存这个信息

那就算了不强求了

根据上述需求,我需要封装一个存储图片的集合,拿到后提供给业务做具体实现

或者也可以在读取到这个图片的时候,执行业务逻辑

maven 依赖坐标:

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.1</version>
</dependency>

poi 的坐标没找着...

下面就是封装的工具类:

import cn.hutool.poi.excel.ExcelReader;
import lombok.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.*; import java.io.FileOutputStream;
import java.util.*;
import java.util.function.Consumer; public class ExcelPicUtil { /**
* @description 读取Excel中的图片 返回sheet下标和对应图片的集合
* @author OnCloud9
* @date 2024/3/20 13:39
* @params ExcelReader excelReader
* @return Map<Integer, List<ExcelPic>>
*/
public static Map<Integer, List<ExcelPic>> getExcelPic(ExcelReader excelReader) {
Workbook workbook = excelReader.getWorkbook();
if (Objects.isNull(workbook)) return null;
boolean isXSSF = workbook instanceof XSSFWorkbook;
boolean isHSSF = workbook instanceof HSSFWorkbook;
if (isXSSF) return getExcelPicByXssfType((XSSFWorkbook) workbook, null);
else if (isHSSF) return getExcelPicByHssfType((HSSFWorkbook) workbook, null);
return null;
} /**
* @description
* @author OnCloud9
* @date 2024/3/20 14:12
* @params
* @return
*/
public static Map<Integer, List<ExcelPic>> getExcelPic(ExcelReader excelReader, Consumer<ExcelPic> consumer) {
Workbook workbook = excelReader.getWorkbook();
if (Objects.isNull(workbook)) return null;
boolean isXSSF = workbook instanceof XSSFWorkbook;
boolean isHSSF = workbook instanceof HSSFWorkbook;
if (isXSSF) return getExcelPicByXssfType((XSSFWorkbook) workbook, consumer);
else if (isHSSF) return getExcelPicByHssfType((HSSFWorkbook) workbook, consumer);
return null;
} @Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static final class ExcelPic {
private Integer sheetIdx;
private String suffix;
private String mimeType;
private byte[] data;
private Integer rowIdx;
private Integer colIdx;
} /**
* @description XSSF工作簿读取图片
* @author OnCloud9
* @date 2024/3/20 13:57
* @params
* @return
*/
private static Map<Integer, List<ExcelPic>> getExcelPicByXssfType(XSSFWorkbook xssfWorkbook, Consumer<ExcelPic> excelPicConsumer) {
int numberOfSheets = xssfWorkbook.getNumberOfSheets();
Map<Integer, List<ExcelPic>> picMap = new HashMap<>(); for (int xssfSheetIdx = 0; xssfSheetIdx < numberOfSheets; xssfSheetIdx++) {
XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(xssfSheetIdx);
XSSFDrawing drawingPatriarch = xssfSheet.getDrawingPatriarch();
if (Objects.isNull(drawingPatriarch)) {
picMap.put(xssfSheetIdx, Collections.emptyList());
continue;
}
List<XSSFShape> shapes = drawingPatriarch.getShapes();
List<ExcelPic> excelPicList = new ArrayList<>(shapes.size());
for (XSSFShape xssfShape : shapes) {
XSSFPicture xssfPicture = (XSSFPicture) xssfShape;
XSSFClientAnchor clientAnchor = xssfPicture.getClientAnchor();
XSSFPictureData xssfPictureData = xssfPicture.getPictureData(); String fileExtension = xssfPictureData.suggestFileExtension();
byte[] data = xssfPictureData.getData();
String mimeType = xssfPictureData.getMimeType(); short col2 = clientAnchor.getCol2();
int row2 = clientAnchor.getRow2(); ExcelPic build = ExcelPic.builder()
.sheetIdx(xssfSheetIdx)
.suffix(fileExtension)
.mimeType(mimeType)
.data(data)
.rowIdx(row2)
.colIdx((int) col2)
.build();
if (Objects.nonNull(excelPicConsumer)) excelPicConsumer.accept(build);
excelPicList.add(build);
}
picMap.put(xssfSheetIdx, excelPicList);
}
return picMap;
} /**
* @description HSSF工作簿读取图片
* @author OnCloud9
* @date 2024/3/20 13:58
* @params
* @return
*/
private static Map<Integer, List<ExcelPic>> getExcelPicByHssfType(HSSFWorkbook hssfWorkbook, Consumer<ExcelPic> excelPicConsumer) {
int numberOfSheets = hssfWorkbook.getNumberOfSheets();
Map<Integer, List<ExcelPic>> picMap = new HashMap<>(); for (int hssfSheetIdx = 0; hssfSheetIdx < numberOfSheets; hssfSheetIdx++) {
HSSFSheet hssfSheet = hssfWorkbook.getSheetAt(hssfSheetIdx);
HSSFPatriarch drawingPatriarch = hssfSheet.getDrawingPatriarch();
if (Objects.isNull(drawingPatriarch)) {
picMap.put(hssfSheetIdx, Collections.emptyList());
continue;
}
List<HSSFShape> hssfShapeList = drawingPatriarch.getChildren();
List<ExcelPic> excelPicList = new ArrayList<>(hssfShapeList.size());
for (HSSFShape hssfShape : hssfShapeList) {
HSSFPicture hssfPicture = (HSSFPicture) hssfShape;
HSSFClientAnchor clientAnchor = hssfPicture.getClientAnchor();
HSSFPictureData hssfPictureData = hssfPicture.getPictureData(); String fileExtension = hssfPictureData.suggestFileExtension();
byte[] data = hssfPictureData.getData();
String mimeType = hssfPictureData.getMimeType(); short col2 = clientAnchor.getCol2();
int row2 = clientAnchor.getRow2(); ExcelPic build = ExcelPic.builder()
.sheetIdx(hssfSheetIdx)
.suffix(fileExtension)
.mimeType(mimeType)
.data(data)
.rowIdx(row2)
.colIdx((int) col2)
.build(); if (Objects.nonNull(excelPicConsumer)) excelPicConsumer.accept(build);
excelPicList.add(build);
}
picMap.put(hssfSheetIdx, excelPicList);
}
return picMap;
} /**
* @description 根据文件路径和图片字节输出
* @author OnCloud9
* @date 2024/3/20 13:56
* @params
* @return
*/
@SneakyThrows
public static void writePicByteTo(byte[] picBytes, String outPutPath) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(outPutPath);
fos.write(picBytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (Objects.nonNull(fos)) fos.close();
}
}
}

 

测试代码DEMO:

    @Test
public void excelPicRead() {
String file = "C:\\Users\\Administrator\\Desktop\\工作日志\\图片导入测试.xlsx";
ExcelReader excelReader = ExcelUtil.getReader(file); List<List<Object>> read = excelReader.read();
System.out.println(read); Map<Integer, List<ExcelPicUtil.ExcelPic>> excelPicMap = ExcelPicUtil.getExcelPic(excelReader, excelPic -> {
// todo ...... 自己实现读取到这个图片时做啥
}); String rootPath = "D:\\ymcd-project\\config\\ouput\\";
excelPicMap.values().forEach(excelPics -> excelPics.forEach(excelPic -> {
ExcelPicUtil.writePicByteTo(excelPic.getData(), rootPath + excelPic.getRowIdx() + "-" + excelPic.getColIdx() + "." + excelPic.getSuffix());
})); System.out.println(excelPicMap.size());
}

  

演示文件:

读取后输出到目录:

三、图片写入

代码参考自51CTO

https://blog.51cto.com/u_16213405/9673126

  

关于图片写入Anchor参数的详细说明见:

https://www.bilibili.com/video/BV1eA41157va?p=25

我想到有图片读取导入,就有图片导出的功能

然后导出的时候和导入一样,一定是基于单元格定位的。

然后图片的呈现是根据单元格大小控制的(不影响图片质量)

这里继续对工具类追加了方法:

    /**
* @description 将图片写入到Excel中
* @author OnCloud9
* @date 2024/3/20 16:40
* @params
* @return
*/
@SneakyThrows
public static void writePicToExcel(ExcelWriter excelWriter, List<ExcelPicWrite> excelPicWriteList) {
Workbook workbook = excelWriter.getWorkbook();
CreationHelper creationHelper = workbook.getCreationHelper();
Sheet sheet;
for (ExcelPicWrite picWrite : excelPicWriteList) {
/* 判断是否提供sheet名称,不提供默认写入到第一个,遍历时不一定存在,所以要判断,不存在时创建出来 */
String sheetName = picWrite.getSheetName();
if (StringUtils.isBlank(sheetName)) sheet = workbook.getSheetAt(0);
else {
sheet = workbook.getSheet(sheetName);
if (Objects.isNull(sheet)) sheet = workbook.createSheet(sheetName);
} /* 获取图片内容,写入工作簿中 */
byte[] picContent = picWrite.getData();
Integer picType = picWrite.getPicType();
int picIdx = workbook.addPicture(picContent, picType); /* 设置图片存放的位置 */
Integer rowIdx = picWrite.getRowIdx();
Integer colIdx = picWrite.getColIdx();
ClientAnchor clientAnchor = creationHelper.createClientAnchor();
clientAnchor.setRow1(rowIdx);
clientAnchor.setRow2(rowIdx + 1);
clientAnchor.setCol1(colIdx);
clientAnchor.setCol2(colIdx + 1); /* 图片绘制渲染 */
Drawing<?> drawingPatriarch = sheet.createDrawingPatriarch();
Picture picture = drawingPatriarch.createPicture(clientAnchor, picIdx);
}
} /**
* @description 图片类型翻译
* @author OnCloud9
* @date 2024/3/20 17:46
* @params
* @return
*/
public static int picTypeTranslate(String picType) {
if (StringUtils.isBlank(picType)) return Workbook.PICTURE_TYPE_JPEG;
switch (picType) {
case "png":
case "PNG":
return Workbook.PICTURE_TYPE_PNG;
case "bmp":
case "BMP":
return Workbook.PICTURE_TYPE_DIB;
default:
case "jpeg":
case "JPEG":
case "jpg":
case "JPG":
return Workbook.PICTURE_TYPE_JPEG;
}
} @Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static final class ExcelPicWrite {
private String sheetName; /* 导出创建时依照sheetName名称为标识,如果不写则默认放到第一个sheet页 */
private byte[] data; /* 图片字节数组 这里不关心图片输入的方式 */
private Integer rowIdx; /* 定位的单元格行 */
private Integer colIdx; /* 定位的单元格列 */
private Integer picType; /* 图片类型 见上面翻译方法 */
}

  

测试代码:

这里图省事我就都用同一个图片源了,导出的Excel上的图片会跟随单元格宽高变化而变化

右键另存为的图片大小也是一样的

    @Test
public void excelPicWrite() {
ExcelWriter excelWriter = ExcelUtil.getWriter();
String picPath = "D:\\ymcd-project\\config\\ouput\\3-2.jpeg";
byte[] demoData = FileUtil.readBytes(picPath);
ExcelPicUtil.writePicToExcel(excelWriter, Arrays.asList(
ExcelPicUtil.ExcelPicWrite.builder()
.picType(Workbook.PICTURE_TYPE_JPEG)
.data(demoData)
.rowIdx(1)
.colIdx(3)
.build(),
ExcelPicUtil.ExcelPicWrite.builder()
.picType(Workbook.PICTURE_TYPE_JPEG)
.data(demoData)
.rowIdx(2)
.colIdx(3).build(),
ExcelPicUtil.ExcelPicWrite.builder()
.picType(Workbook.PICTURE_TYPE_JPEG)
.data(demoData)
.rowIdx(3)
.colIdx(3)
.build()
)); excelWriter.flush(new File("D:\\ymcd-project\\config\\ouput\\write-test.xlsx"));
excelWriter.close();
}

  

【Java】Excel 读写图片工具类的更多相关文章

  1. java excel转pdf 工具类

    package com.elitel.hljhr.comm.web.main.controller; import java.io.File; import java.io.FileOutputStr ...

  2. java Excel导入导出工具类

    本文章,导入导出依赖提前定义好的模板 package com.shareworx.yjwy.utils; import java.io.File; import java.io.FileInputSt ...

  3. Java图片工具类,完成图片的截取和任意缩放

    package com.common.util; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Renderin ...

  4. Java基础学习总结(49)——Excel导入导出工具类

    在项目的pom文件中引入 <dependency> <groupId>net.sourceforge.jexcelapi</groupId> <artifac ...

  5. 拍照、本地图片工具类(兼容至Android7.0)

    拍照.本地图片工具类:解决了4.4以上剪裁会提示"找不到文件"和6.0动态授予权限,及7.0报FileUriExposedException异常问题. package com.hb ...

  6. 一个基于POI的通用excel导入导出工具类的简单实现及使用方法

    前言: 最近PM来了一个需求,简单来说就是在录入数据时一条一条插入到系统显得非常麻烦,让我实现一个直接通过excel导入的方法一次性录入所有数据.网上关于excel导入导出的例子很多,但大多相互借鉴. ...

  7. Workbook导出excel封装的工具类

    在实际中导出excel非常常见,于是自己封装了一个导出数据到excel的工具类,先附上代码,最后会写出实例和解释.支持03和07两个版本的 excel. HSSF导出的是xls的excel,XSSF导 ...

  8. Android--很实用的图片工具类

    import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; imp ...

  9. Excel解析easyexcel工具类

    Excel解析easyexcel工具类 easyexcel解决POI解析Excel出现OOM <!-- https://mvnrepository.com/artifact/com.alibab ...

  10. 最全的Java操作Redis的工具类,使用StringRedisTemplate实现,封装了对Redis五种基本类型的各种操作!

    转载自:https://github.com/whvcse/RedisUtil 代码 ProtoStuffSerializerUtil.java import java.io.ByteArrayInp ...

随机推荐

  1. C语言——函数基本知识

    什么是函数 函数的定义 函数就是程序中独立的功能.(简单来说就是把代码打包成整体,起个名字,方便以后使用) 函数的使用 定义函数的格式 格式: 返回值类型 函数名(形参1,形参2,...) { 函数体 ...

  2. Vue 3与ESLint、Prettier:构建规范化的前端开发环境

    title: Vue 3与ESLint.Prettier:构建规范化的前端开发环境 date: 2024/6/11 updated: 2024/6/11 publisher: cmdragon exc ...

  3. Javascript高级程序设计第四章 | ch4 | 阅读笔记

    变量.作用域与内存 原始值与引用值 什么是字面量形式? let obj = { key1: val1, key2: val2, foo () { } } 这就是字面量形式,手动声明一个对象的属性和方法 ...

  4. 安装nodejs易遇到的坑

    @ 目录 背景描述 流程步骤 小结 背景描述 我的服务器是centos7.9,打算先直接通过yum安装,但是yum不能指定node版本,我直接指定显示404找不到,然后我设置了下node下载的源,还是 ...

  5. linux日志查询less及堡垒机查询日志方法

    方法1tail -f orderFile.log | grep "关键字" postman接口请求的时候,关注控制台对关键字过滤的打印输出. 方法2less 文件名称/ 关键字n ...

  6. 简单的解释下什么是CNAME

    今天在用阿里云的安全防护给接口域名做web应用防火墙,需要配置cname,原来有用到过但是一直没去了解过,只知道怎么用今天搜了一下看看下面是原文,白话文好理解分享一下. 什么是CNAME?先简单的说下 ...

  7. 三种方法教你下载 Windows 10 和 Windows 11 原生镜像

    原文地址:https://itxiaozhang.com/three-methods-to-download-windows-10-and-windows-11-iso/ 本文配合视频食用效果最佳,视 ...

  8. python _XMLParser.__init__()初始化失败,提示“takes 1 positional argument but 4 were given”

    问题: 在一个新的环境下,执行openpyxl相关的操作,初始化时,逐步执行,需要调到 ElementTree.py _XMLParser.__init__(self, html, target, e ...

  9. CAP 8.2 版本发布通告

    前言 今天我们很高兴宣布 CAP 发布 8.2 版本正式版,我们在这个版本中主要致力于对订阅着并行执行的特性提供支持,同时添加了对在订阅者中对消息头的控制行为. 下面,具体看一下我们新版本的功能吧. ...

  10. opengrok源代码在线阅读平台搭建及字体修改

    服务搭建 我所编写的docker-compose.yml如下,成功运行后将源码目录移动至 /data/opengrok/src ,重启容器使得opengrok快速更新索引 services: open ...