利用Java實(shí)現(xiàn)讀取WPS?Excel中嵌入的圖片
引言
許多數(shù)據(jù)文件中可能包含嵌入式圖片,這些圖片對于數(shù)據(jù)分析和可視化非常重要。然而,從 WPS 在 Excel 中讀取這些圖片可能會有一些技術(shù)挑戰(zhàn)。在本文中,我將展示如何從 WPS Excel 文件中讀取嵌入的圖片,并提供代碼示例。
提取圖片資源的方法
以下是用于從 WPS Excel 文件中讀取圖片資源的方法。我們使用了 Apache POI 庫來處理 Excel 文件,并使用了 cn.hutool.json 庫來處理 XML 數(shù)據(jù)。這些庫可以幫助我們解析 Excel 文件中的嵌入式圖片。
主函數(shù)
首先,我們定義了主函數(shù) main,用于測試圖片提取功能:
public static void main(String[] args) { PicturesUtils picturesUtils = new PicturesUtils(); byte[] fileData = picturesUtils.getFileStream(new File("你的文件路徑")); Map<String, XSSFPictureData> pictures = picturesUtils.getPictures(fileData); pictures.forEach((id, xssfPictureData) -> { System.out.println("id:" + id); String fileName = xssfPictureData.getPackagePart().getPartName().getName(); System.out.println("fileName:" + fileName); File file = new File("D:\\" + fileName); File dir = file.getParentFile(); dir.mkdirs(); try { Files.write(Path.of(file.getPath()), xssfPictureData.getData()); } catch (IOException e) { e.printStackTrace(); } }); }
在這個函數(shù)中,我們創(chuàng)建了一個 PicturesUtils 實(shí)例,并從文件路徑中獲取文件數(shù)據(jù)。然后,我們調(diào)用 getPictures 方法來提取圖片資源,并遍歷結(jié)果將圖片寫入本地文件。
獲取浮動圖片
接下來,我們展示了如何從 Excel 文件中獲取浮動圖片:
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 picture) { XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor(); XSSFPictureData pictureData = picture.getPictureData(); String key = anchor.getRow1() + "-" + anchor.getCol1(); mapFloatingPictures.put(key, pictureData); } } } return mapFloatingPictures; }
該方法接收一個 XSSFSheet 對象,并返回一個包含浮動圖片的映射。它遍歷工作表中的所有形狀,并將圖片數(shù)據(jù)存儲在映射中。
處理圖片數(shù)據(jù)
最后,我們展示了如何處理 Excel 文件中的圖片數(shù)據(jù),包括嵌入式圖片和浮動式圖片:
public 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<>(); } }
在該方法中,我們使用 processZipEntries 和 processPictures 方法來處理 Zip 文件中的條目和圖片數(shù)據(jù)。然后,通過遍歷 Excel 文件中的所有工作表,獲取浮動圖片。
完整代碼
實(shí)際在程序中讀取的內(nèi)容為=DISPIMG(“ID_03BC802DDAB24510A9883DB157EAC0F8”,1)公式
將公式中的id提取出來ID_03BC802DDAB24510A9883DB157EAC0F8,最后就可以使用下面方法拿到當(dāng)前id獲取到的圖片資源 jdk版本17
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.nio.file.Files; import java.nio.file.Path; 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 PicturesUtils { public static void main(String[] args) { PicturesUtils picturesUtils = new PicturesUtils(); byte[] fileData = picturesUtils.getFileStream(new File("你的文件路徑")); Map<String, XSSFPictureData> pictures = picturesUtils.getPictures(fileData); pictures.forEach((id, xssfPictureData) -> { System.out.println("id:" + id); String fileName = xssfPictureData.getPackagePart().getPartName().getName(); System.out.println("fileName:" + fileName); File file = new File("C:\\Users\\XXX\\Desktop\\" + fileName); File dir = file.getParentFile(); dir.mkdirs(); try { Files.write(Path.of(file.getPath()), xssfPictureData.getData()); } catch (IOException e) { e.printStackTrace(); } }); } /** * 獲取浮動圖片,以 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 picture) { XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor(); XSSFPictureData pictureData = picture.getPictureData(); String key = anchor.getRow1() + "-" + anchor.getCol1(); mapFloatingPictures.put(key, pictureData); } } } return mapFloatingPictures; } /** * 處理 WPS 文件中的圖片數(shù)據(jù),返回圖片信息 map。 * * @param stream 輸入流 * @param mapConfig 配置映射 * @return 圖片信息的 map * @throws IOException */ private 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 二進(jìn)制數(shù)據(jù) * @return 圖片信息的 map * @throws IOException */ public 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 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 void processCellImages(ZipInputStream zipInputStream, Map<String, String> mapConfig) throws IOException { String content = IOUtils.toString(zipInputStream, StandardCharsets.UTF_8); 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 cellImageObj) { if (cellImageObj != null) { cellImageArray = new JSONArray(); cellImageArray.add(cellImageObj); } } if (cellImageArray != null) { processImageItems(cellImageArray, mapConfig); } } } } /** * 處理 cellImageArray 中的圖片項(xiàng),更新圖片配置信息。 * * @param cellImageArray 圖片項(xiàng)的 JSONArray * @param mapConfig 配置信息的 map */ private 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 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 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 Map<String, String> processCellImagesRels(ZipInputStream zipInputStream, Map<String, String> mapConfig) throws IOException { String content = IOUtils.toString(zipInputStream, StandardCharsets.UTF_8); 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 relationshipObj) { if (relationshipObj != null) { relationshipArray = new JSONArray(); relationshipArray.add(relationshipObj); } } if (relationshipArray != null) { return processRelationships(relationshipArray, mapConfig); } } return null; } /** * 處理 relationshipArray 中的關(guān)系項(xiàng),更新配置信息。 * * @param relationshipArray 關(guān)系項(xiàng)的 JSONArray * @param mapConfig 配置信息的 map * @return 配置信息的 map */ private 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; } /** * @param file 數(shù)據(jù)文件 * @return {@link byte[]} * @description * @author bianhl * @date 2024/4/26 13:52 */ private byte[] getFileStream(File file) { try (InputStream inputStream = new FileInputStream(file)) { // 創(chuàng)建 ByteArrayOutputStream 來暫存流數(shù)據(jù) 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 的內(nèi)容獲取為字節(jié)數(shù)組 return byteArrayOutputStream.toByteArray(); } catch (IOException e) { return null; } } }
輸出結(jié)果:
拿到的圖片數(shù)據(jù)
以上就是利用Java實(shí)現(xiàn)讀取WPS Excel中嵌入的圖片的詳細(xì)內(nèi)容,更多關(guān)于Java讀取Excel嵌入的圖片的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于@Autowired注入依賴失敗的問題及解決
這篇文章主要介紹了關(guān)于@Autowired注入依賴失敗的問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08解決org.springframework.context.ApplicationContextException報(bào)錯的
這篇文章主要介紹了解決org.springframework.context.ApplicationContextException報(bào)錯的問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06Java ThreadPoolExecutor的參數(shù)深入理解
這篇文章主要介紹了Java ThreadPoolExecutor的參數(shù)深入理解的相關(guān)資料,需要的朋友可以參考下2017-03-03Java中Thread和Runnable創(chuàng)建線程的方式對比
本文主要介紹了Java中Thread和Runnable創(chuàng)建線程的方式對比,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07