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<String, XSSFPictureData> getExcelPictures(InputStream is) {
|
byte[] fileData = getFileStream(is);
|
Map<String, XSSFPictureData> 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;
|
}
|
|
/**
|
* 获取浮动图片,以 map 形式返回,键为行列格式 x-y。
|
*
|
* @param xssfSheet WPS 工作表
|
* @return 浮动图片的 map
|
*/
|
public static Map<String, XSSFPictureData> getFloatingPictures(XSSFSheet xssfSheet) {
|
Map<String, XSSFPictureData> mapFloatingPictures = new HashMap<>();
|
XSSFDrawing drawingPatriarch = xssfSheet.getDrawingPatriarch();
|
if (drawingPatriarch != null) {
|
List<XSSFShape> 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<String, XSSFPictureData> processPictures(ByteArrayInputStream stream, Map<String, String> mapConfig) throws IOException {
|
Map<String, XSSFPictureData> mapPictures = new HashedMap<>();
|
Workbook workbook = WorkbookFactory.create(stream);
|
List<XSSFPictureData> allPictures = (List<XSSFPictureData>) 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<String, XSSFPictureData> getPictures(byte[] data) {
|
try {
|
Map<String, String> mapConfig = processZipEntries(new ByteArrayInputStream(data));
|
Map<String, XSSFPictureData> mapPictures = processPictures(new ByteArrayInputStream(data), mapConfig);
|
Iterator<Sheet> 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<String, String> processZipEntries(ByteArrayInputStream stream) throws IOException {
|
Map<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> processCellImagesRels(ZipInputStream zipInputStream, Map<String, String> 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<String, String> processRelationships(JSONArray relationshipArray, Map<String, String> mapConfig) {
|
Map<String, String> 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();
|
// 将 inputStream 读取到 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;
|
}
|
}
|
|
}
|