一、需求背景:

做一个大屏管理系统,基础信息包括管理的应用名称,大屏的截图,通过一个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. QuartusII调用 PLL_IP核方法(Mega Wizard)

    [基本信息] 要求:调用PLL-IP核,50Mhz晶振输入,输出四路时钟不同信号:100Mhz,25Mhz,50Mhz(90°相位),50Mhz(20%占空比). 芯片型号:cyclone Ⅳ EP4 ...

  2. .NET集成DeveloperSharp实现"高效分页"&"无主键分页"

    DeveloperSharp系列近期又被制造业ERP.民航飞行App.建筑BIM.电力掌上营业厅.等多家大型采用,站在巨人的肩膀上你能走的更远. 支持.Net Core2.0及以上,支持.Net Fr ...

  3. CF437E The Child and Polygon

    The Child and Polygon 题解 这世界这么大,遇到了这个奇奇怪怪的题. 这道题其实可以很自然的联想到卡特兰数. 在卡特兰数的计数中,有这么一个意义:\(C_n\) 表示把有 \(n+ ...

  4. C#窗体内控件大小随窗体等比例变化

    一.首先定义全局变量 1 private float X;//当前窗体的宽度 2 private float Y;//当前窗体的高度 3 private bool IsFirst = true; 二. ...

  5. INFINI Labs 产品更新 | Console 数据迁移支持 Percentiles 均匀分区

    INFINI Labs 产品又更新啦~,包括 Console v1.14.0,Gateway 1.21.0.其中 Console 数据迁移支持 Percentiles 均匀分区,修复已知 Bug 等. ...

  6. INFINI Labs 产品更新 | Gateway 支持基于 Kafka 的复制能力,发布 Helm Charts 部署方式

    INFINI Labs 产品又更新啦~.本次更新概要如下:Easysearch 新增了索引字段相关统计 API,优化了 source_reuse 提升压缩效率:Gateway 新增诸多新特性,如:支持 ...

  7. SRE 必备利器:域名 DNS 探测排障工具

    问题背景 访问某个 HTTP 域名接口,偶发性超时,原因可能多种多样,比如 DNS 解析问题.网络质量问题.对端服务负载问题等,在客户端没有良好埋点的情况下,排查起来比较费劲,只能挨个方向尝试,这里送 ...

  8. 如何使用csproj构建C#源代码组件NuGet包?

    一般我们构建传统的NuGet包,都是打包和分发dll程序集文件. 至于打包和分发C#源代码文件的做法,比较少见. 那么这种打包源代码文件的做法,有什么优点和缺点呢? 优点: 方便阅读源代码. 方便断点 ...

  9. 大数据面试吹牛草稿V2.0

    面试吹牛之前先打个草稿! 各位面试官好! 我叫 xxx,毕业于 xxx,之前在 xxx 公司待了 1 年多,期间⼀直从事的是 IT 行业,刚开始的时候做的是 Java 开发后来转岗到大数据方向做大数据 ...

  10. GIS数据获取:土地利用与土壤属性、DEM、水体水系数据

      本文对目前主要的土壤属性.地表覆盖.数字高程模型与水体水系矢量数据获取网站加以整理与介绍.   本文为"GIS数据获取整理"专栏中第三篇独立博客,因此本文全部标题均由" ...