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实现导入带有图像的 Excel 文件_java导入excel包含图片-CSDN博客

作者:芭比木聂牛原文地址:https://blog.csdn.net/m0_65167892/article/details/143131771

%s 个评论

要回复文章请先登录注册