Java 使用POI导入带有图片的Excel(单张/多张) 使用poi导入数据,excel带有图片元素
tips:该文仅记录学习,有什么不足、不成熟或者错误的地方欢迎大家指正。
1.需求
导入模板如下,要求将数据持久化到数据库,图片使用oss存储到minio,在查询时指定字段带出图片url。
2.使用的工具
org.apache.poi poi-ooxml 5.2.3 org.apache.commons commons-lang3
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 analyzePicture
*/
public Map analyzePicture(MultipartFile file){
//收集结果
Map arrangedData = new HashMap();
try {
//获取导入文件的输入流
InputStream inputStream = file.getInputStream();
//转工作薄对象
XSSFWorkbook sheets = new XSSFWorkbook(inputStream);
//获取页签(如果是多个页签可以循环获取,这里只演示一个)
Sheet sheet = sheets.getSheetAt(0);
//获取画布
XSSFDrawing patriarch = (XSSFDrawing)sheet.createDrawingPatriarch();
List 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 handleExcelPicture(MultipartFile file, List sahPostDetailVos) {
//这里为上述的获取图片数据操作
Map pictureData = excelPictureUtil.analyzePicture(file);
//遍历
for (Integer row : pictureData.keySet()) {
//获取图片的行号
Map integerListMap = pictureData.get(row);
//获取图片所属的数据对象
SahPostInfoVo sahPostInfoVo = sahPostDetailVos.get(row-1);
for (Integer column : integerListMap.keySet()) {
//获取图片的列号
//进行逻辑判断
List 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 使用POI导入带有图片的Excel(单张/多张) 使用poi导入数据,excel带有图片元素Java 使用POI导入带有图片的Excel(单张/多张) 使用poi导入数据,excel带有图片元素Java 使用POI导入带有图片的Excel(单张/多张) 使用poi导入数据,excel带有图片元素Java 使用POI导入带有图片的Excel(单张/多张) 使用poi导入数据,excel带有图片元素Java 使用POI导入带有图片的Excel(单张/多张) 使用poi导入数据,excel带有图片元素Java 使用POI导入带有图片的Excel(单张/多张) 使用poi导入数据,excel带有图片元素Java 使用POI导入带有图片的Excel(单张/多张) 使用poi导入数据,excel带有图片元素Java 使用POI导入带有图片的Excel(单张/多张) 使用poi导入数据,excel带有图片元素Java 使用POI导入带有图片的Excel(单张/多张) 使用poi导入数据,excel带有图片元素