package com.doumee.core.annotation.excel; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.XML; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.map.HashedMap; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.openxml4j.opc.PackagePartName; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.apache.poi.xssf.usermodel.*; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** * @author bianhl * @version 1.0 * @description 获取图片资源 * @date 2024年4月28日08:44:42 */ @Slf4j public class ExcelPictureUtil { public static Map getExcelPictures(InputStream is) { try { byte[] fileData = getFileStream(is); Map pictures = getPictures(fileData); pictures.forEach((id, xssfPictureData) -> { System.out.println("id:" + id); String fileName = xssfPictureData.getPackagePart().getPartName().getName(); System.out.println("fileName:" + fileName); }); return pictures; }catch (Exception e){ return null; } } /** * 获取浮动图片,以 map 形式返回,键为行列格式 x-y。 * * @param xssfSheet WPS 工作表 * @return 浮动图片的 map */ public static Map getFloatingPictures(XSSFSheet xssfSheet) { Map mapFloatingPictures = new HashMap<>(); XSSFDrawing drawingPatriarch = xssfSheet.getDrawingPatriarch(); if (drawingPatriarch != null) { List shapes = drawingPatriarch.getShapes(); for (XSSFShape shape : shapes) { if (shape instanceof XSSFPicture ) { XSSFPicture picture = (XSSFPicture)shape; XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor(); XSSFPictureData pictureData = picture.getPictureData(); String key = anchor.getRow1() + "-" + anchor.getCol1(); mapFloatingPictures.put(key, pictureData); } } } return mapFloatingPictures; } /** * 处理 WPS 文件中的图片数据,返回图片信息 map。 * * @param stream 输入流 * @param mapConfig 配置映射 * @return 图片信息的 map * @throws IOException */ private static Map processPictures(ByteArrayInputStream stream, Map mapConfig) throws IOException { Map mapPictures = new HashedMap<>(); Workbook workbook = WorkbookFactory.create(stream); List allPictures = (List) workbook.getAllPictures(); for (XSSFPictureData pictureData : allPictures) { PackagePartName partName = pictureData.getPackagePart().getPartName(); String uri = partName.getURI().toString(); if (mapConfig.containsKey(uri)) { String strId = mapConfig.get(uri); mapPictures.put(strId, pictureData); } } return mapPictures; } /** * 获取 WPS 文档中的图片,包括嵌入式图片和浮动式图片。 * * @param data 二进制数据 * @return 图片信息的 map * @throws IOException */ public static Map getPictures(byte[] data) { try { Map mapConfig = processZipEntries(new ByteArrayInputStream(data)); Map mapPictures = processPictures(new ByteArrayInputStream(data), mapConfig); Iterator sheetIterator = WorkbookFactory.create(new ByteArrayInputStream(data)).sheetIterator(); while (sheetIterator.hasNext()) { mapPictures.putAll(getFloatingPictures((XSSFSheet) sheetIterator.next())); } return mapPictures; } catch (IOException e) { return new HashedMap<>(); } } /** * 处理 Zip 文件中的条目,更新图片配置信息。 * * @param stream Zip 输入流 * @return 配置信息的 map * @throws IOException */ private static Map processZipEntries(ByteArrayInputStream stream) throws IOException { Map mapConfig = new HashedMap<>(); ZipInputStream zipInputStream = new ZipInputStream(stream); ZipEntry zipEntry; while ((zipEntry = zipInputStream.getNextEntry()) != null) { try { final String fileName = zipEntry.getName(); if ("xl/cellimages.xml".equals(fileName)) { processCellImages(zipInputStream, mapConfig); } else if ("xl/_rels/cellimages.xml.rels".equals(fileName)) { return processCellImagesRels(zipInputStream, mapConfig); } } finally { zipInputStream.closeEntry(); } } return new HashedMap<>(); } /** * 处理 Zip 文件中的 cellimages.xml 文件,更新图片配置信息。 * * @param zipInputStream Zip 输入流 * @param mapConfig 配置信息的 map * @throws IOException */ private static void processCellImages(ZipInputStream zipInputStream, Map mapConfig) throws IOException { String content = IOUtils.toString(zipInputStream); JSONObject jsonObject = XML.toJSONObject(content); if (jsonObject != null) { JSONObject cellImages = jsonObject.getJSONObject("etc:cellImages"); if (cellImages != null) { JSONArray cellImageArray = null; Object cellImage = cellImages.get("etc:cellImage"); if (cellImage != null && cellImage instanceof JSONArray) { cellImageArray = (JSONArray) cellImage; } else if (cellImage != null && cellImage instanceof JSONObject) { JSONObject cellImageObj = (JSONObject) cellImage; if (cellImageObj != null) { cellImageArray = new JSONArray(); cellImageArray.add(cellImageObj); } } if (cellImageArray != null) { processImageItems(cellImageArray, mapConfig); } } } } /** * 处理 cellImageArray 中的图片项,更新图片配置信息。 * * @param cellImageArray 图片项的 JSONArray * @param mapConfig 配置信息的 map */ private static void processImageItems(JSONArray cellImageArray, Map mapConfig) { for (int i = 0; i < cellImageArray.size(); i++) { JSONObject imageItem = cellImageArray.getJSONObject(i); if (imageItem != null) { JSONObject pic = imageItem.getJSONObject("xdr:pic"); if (pic != null) { processPic(pic, mapConfig); } } } } /** * 处理 pic 中的图片信息,更新图片配置信息。 * * @param pic 图片的 JSONObject * @param mapConfig 配置信息的 map */ private static void processPic(JSONObject pic, Map mapConfig) { JSONObject nvPicPr = pic.getJSONObject("xdr:nvPicPr"); if (nvPicPr != null) { JSONObject cNvPr = nvPicPr.getJSONObject("xdr:cNvPr"); if (cNvPr != null) { String name = cNvPr.getStr("name"); if (StringUtils.isNotEmpty(name)) { String strImageEmbed = updateImageEmbed(pic); if (strImageEmbed != null) { mapConfig.put(strImageEmbed, name); } } } } } /** * 获取嵌入式图片的 embed 信息。 * * @param pic 图片的 JSONObject * @return embed 信息 */ private static String updateImageEmbed(JSONObject pic) { JSONObject blipFill = pic.getJSONObject("xdr:blipFill"); if (blipFill != null) { JSONObject blip = blipFill.getJSONObject("a:blip"); if (blip != null) { return blip.getStr("r:embed"); } } return null; } /** * 处理 Zip 文件中的 relationship 条目,更新配置信息。 * * @param zipInputStream Zip 输入流 * @param mapConfig 配置信息的 map * @return 配置信息的 map * @throws IOException */ private static Map processCellImagesRels(ZipInputStream zipInputStream, Map mapConfig) throws IOException { String content = IOUtils.toString(zipInputStream ); JSONObject jsonObject = XML.toJSONObject(content); JSONObject relationships = jsonObject.getJSONObject("Relationships"); if (relationships != null) { JSONArray relationshipArray = null; Object relationship = relationships.get("Relationship"); if (relationship != null && relationship instanceof JSONArray) { relationshipArray = (JSONArray) relationship; } else if (relationship != null && relationship instanceof JSONObject ) { JSONObject relationshipObj = (JSONObject) relationship; if (relationshipObj != null) { relationshipArray = new JSONArray(); relationshipArray.add(relationshipObj); } } if (relationshipArray != null) { return processRelationships(relationshipArray, mapConfig); } } return null; } /** * 处理 relationshipArray 中的关系项,更新配置信息。 * * @param relationshipArray 关系项的 JSONArray * @param mapConfig 配置信息的 map * @return 配置信息的 map */ private static Map processRelationships(JSONArray relationshipArray, Map mapConfig) { Map mapRelationships = new HashedMap<>(); for (int i = 0; i < relationshipArray.size(); i++) { JSONObject relaItem = relationshipArray.getJSONObject(i); if (relaItem != null) { String id = relaItem.getStr("Id"); String value = "/xl/" + relaItem.getStr("Target"); if (mapConfig.containsKey(id)) { String strImageId = mapConfig.get(id); mapRelationships.put(value, strImageId); } } } return mapRelationships; } /** * @return {@link byte[]} * @description * @author bianhl * @date 2024/4/26 13:52 */ private static byte[] getFileStream(InputStream inputStream) { // 创建 ByteArrayOutputStream 来暂存流数据 try { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) != -1) { byteArrayOutputStream.write(buffer, 0, length); } // 将 byteArrayOutputStream 的内容获取为字节数组 return byteArrayOutputStream.toByteArray(); } catch (IOException e) { return null; } } }