淘宝客网站建站源码,免费 个人 网站,南京seo报价,网站制作加教程视频教程#x1f308; 场景概述
java 小伙伴相信都使用 EasyExcel 以及 POI 库实现过 Excel 批量导入、导出功能#xff0c;但只有部分人实现过 excel 导入带图片数据的场景。这个技术实现手段网上也有很多案例和demo#xff0c;最常见的就是通过 XSSFPictureData 来实现。但是在 W… 场景概述
java 小伙伴相信都使用 EasyExcel 以及 POI 库实现过 Excel 批量导入、导出功能但只有部分人实现过 excel 导入带图片数据的场景。这个技术实现手段网上也有很多案例和demo最常见的就是通过 XSSFPictureData 来实现。但是在 WPS 单元格嵌入图片场景下本法无效。
本文讲解如何利用 AI 在5分钟内实现用 Java EasyExcel 针对 WPS Excel 单元格嵌入图片的读取。 本节你将学到
什么凡是用过电脑的人99%都会数据库Excel 真的是你平时见到的那个样子么用 AI 5分钟搞定Java EasyExcel 针对 WPS Excel 单元格嵌入图片的读取。 视频版教程
喜欢看视频教程就看视频喜欢看图文教程就继续往下滑 让AI帮我用java实现EasyExel读取图片支持WPS嵌入图片AI 5分钟搞定普通程序员5天工作量 笔记原文 让AI帮我用java实现EasyExel读取图片—支持WPS嵌入图片 · 语雀 1、常规Excel读取图片的问题
最常见的通过 java easyexcel 实现excel图片读取的方法基本都是通过Apache POI库XSS*方法实现
import org.apache.poi.xssf.usermodel.XSSFPictureData;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;// 假设你已经有了一个MultipartFile类型的Excel文件
XSSFWorkbook workbook new XSSFWorkbook(file.getInputStream());
Sheet sheet workbook.getSheetAt(0);
MapString, XSSFPictureData pictures new HashMap();for (XSSFPictureData picture : workbook.getAllPictures()) {pictures.put(picture.getPackagePart().getPartName().getName(), picture);
}// 遍历sheet中的所有形状找到图片并处理
for (POIXMLDocumentPart dr : sheet.getRelations()) {if (dr instanceof XSSFDrawing) {XSSFDrawing drawing (XSSFDrawing) dr;for (XSSFShape shape : drawing.getShapes()) {if (shape instanceof XSSFPicture) {XSSFPicture picture (XSSFPicture) shape;XSSFPictureData picData pictures.get(picture.getPackagePart().getPartName().getName());// 处理图片数据例如保存到服务器或数据库}}}
}
但是本法对于 WPS 内嵌图片无效因为 WPS 内嵌图片使用了 DISPIMG函数我们用上面的方法解析后得到的只是函数信息并非图片信息。如下图的 DISPIMG(ID_79A9B2935BEA4B1B8836ECE25C09D573,1) 2、揭开 Excel 的神秘面纱
你以为的Excel 真实的Excel
你以为的Excel 真实的Excel 这里要讲一个知识就是 Excel 可以被理解为一种简单的数据库
因为它具有存储和组织数据的能力并且可以通过公式、查询和宏等功能来处理数据。以下是一些将 Excel 视为数据库的理由
数据存储Excel 文件.xlsx 或 .xls可以存储大量的数据类似于数据库中的表。表格结构Excel 中的工作表类似于数据库中的表它们都有行和列的结构。数据操作Excel 提供了排序、筛选和查找等基本的数据操作功能这些也是数据库管理系统DBMS中常见的操作。数据查询Excel 允许使用公式和函数如 VLOOKUP、HLOOKUP、INDEX 和 MATCH来查询和分析数据。数据验证Excel 提供数据验证功能可以限制输入的数据类型类似于数据库中的数据完整性约束。宏和自动化Excel 的宏功能可以用来自动化重复性的数据操作任务类似于数据库中的存储过程和触发器。
然而尽管 Excel 具有这些数据库的特性它也有一些限制使其不适合作为大型或复杂的数据库解决方案
性能问题对于大型数据集Excel 的性能可能会下降因为它不是为处理大规模数据而设计的。数据安全和权限管理Excel 在数据安全和权限管理方面不如专业的数据库管理系统强大。数据一致性和完整性Excel 缺乏数据库管理系统中的数据一致性和完整性约束。多用户访问Excel 文件通常不适合多用户同时访问和编辑而数据库管理系统支持多用户并发访问。扩展性和可伸缩性随着数据量的增长Excel 的扩展性和可伸缩性不如专业的数据库系统。
因此虽然 Excel 可以被视为一种数据库但它更适合于小型、简单的数据管理和分析任务。对于需要高性能、高安全性、复杂查询和大规模数据处理的场景专业的数据库管理系统如 MySQL、PostgreSQL、Oracle 等会是更合适的选择。 前面我们截图中的“真实的Excel”是怎么回事
其实很简单
我们将 WPS 创建的.xlsx文件后缀名改为.zip后解压缩文件得到的就是这个效果。 3、EasyExcel 读取WPS内嵌图片如何解决
3.1 先来找到图片位置以及绑定方式
根据前面的分析我们可以看到 wps 的内嵌图片是通过DISPIMG函数加载的同时我们通过 Excel 神秘面纱中看到了其“Excel 数据的底层文件构造”接下来我们找到这个函数与图片之间的逻辑关系然后通过代码找到这个图片就行了。
文件/xl/cellimages.xml 文件/xl/_rels/cellimages.xml.rels 最后找到图片 3.2 让AI帮助我们用java实现
首先打开领航AGI聚合平台领航AGI点击 playground 进入 AI 工具输入提示要求第一次没有提到wps问题所以生成的代码仍存在问题这里不做演示直接下一步 告诉他wps内嵌图片问题并把文件xml给AI AI完整对话内容点击查看全部 3.3 看最终代码
这里面我后来把文件名称调整了下方便后期大家检索
WPSExcelImportImgListener.java
package com.sinosoft.hanlin.jyy.handler;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.sinosoft.hanlin.jyy.core.vo.common.WPSExcelImportImgDemoVo;
import lombok.Getter;import java.util.ArrayList;
import java.util.List;/*** author 微信 LHYYH0001* description: Excel带图片导入* 处理图片与数据行的关联。由于图片的对应顺序已在 ExcelImageExtractor 中提供这里可以通过行号进行匹配。* create 2024-11-05 09:07**/
Getter
public class WPSExcelImportImgListener extends AnalysisEventListenerWPSExcelImportImgDemoVo {private ListWPSExcelImportImgDemoVo dataList new ArrayList();Overridepublic void invoke(WPSExcelImportImgDemoVo data, AnalysisContext context) {// 处理每一行的数据dataList.add(data);}Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 所有数据解析完成后执行}
}WPSExcelImportImgDemoVo.java
package com.sinosoft.hanlin.jyy.core.vo.common;import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;Data
public class WPSExcelImportImgDemoVo {ExcelProperty(标题)private String title;// // 将图片字段设置为 ListImageData 类型用于接收多张图片
// ExcelProperty(value 图片)
// private ListImageData picture;ExcelProperty(value 图片)private String picture;
}
WPSExcelImportImgExtractor.java
package com.sinosoft.hanlin.jyy.handler;import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;public class WPSExcelImportImgExtractor {/*** 提取 Excel 中的图片并返回单元格与图片路径的映射* p* WPS 的内置函数无法提取图片需要使用第三方库解析XML文件。* WPS 内嵌图片会把图片做DISPIMG函数处理如DISPIMG(ID_9D6E8C240C8945178DFF238232B217BF,1)* 我们可以将.xlsx 文件后缀改成.zip后解压即可看到* 在xl路径下的cellimages.xml文件中可以看到函数中的id值* 并且在cellimages.xml.rels中可以看到函数与图片之间的关系而图片就位于xl/media路径下** param inputStream Excel 文件的输入流* param outputDir 图片保存的目标目录* return 单元格位置如 A1与图片路径的映射* throws Exception 异常*/public MapString, String extractImages(InputStream inputStream, String outputDir) throws Exception {MapString, String cellImageMap new HashMap();MapString, String relsMap new HashMap();MapString, byte[] imagesData new HashMap();// 创建目标目录File dir new File(outputDir);if (!dir.exists()) {dir.mkdirs();}ZipInputStream zis new ZipInputStream(inputStream);ZipEntry zipEntry;ByteArrayOutputStream baos null;String sheetXml null;String cellImagesXml null;String cellImagesRelsXml null;// 首先遍历所有的Zip条目找到需要的XML和图片文件while ((zipEntry zis.getNextEntry()) ! null) {String entryName zipEntry.getName();if (xl/cellimages.xml.equals(entryName)) {baos new ByteArrayOutputStream();IOUtils.copy(zis, baos);cellImagesXml baos.toString(UTF-8);baos.close();} else if (xl/_rels/cellimages.xml.rels.equals(entryName)) {baos new ByteArrayOutputStream();IOUtils.copy(zis, baos);cellImagesRelsXml baos.toString(UTF-8);baos.close();} else if (entryName.startsWith(xl/media/)) {byte[] imageBytes IOUtils.toByteArray(zis);String imageName entryName.substring(xl/media/.length());imagesData.put(imageName, imageBytes);}zis.closeEntry();}zis.close();// 解析cellimages.xml.rels建立rId到图片文件名的映射if (cellImagesRelsXml ! null) {DocumentBuilderFactory factory DocumentBuilderFactory.newInstance();DocumentBuilder builder factory.newDocumentBuilder();Document relsDoc builder.parse(new ByteArrayInputStream(cellImagesRelsXml.getBytes(UTF-8)));NodeList relNodes relsDoc.getElementsByTagName(Relationship);for (int i 0; i relNodes.getLength(); i) {Element relElement (Element) relNodes.item(i);String rId relElement.getAttribute(Id);// e.g., media/image1.pngString target relElement.getAttribute(Target);relsMap.put(rId, target.substring(media/.length()));}}// 解析cellimages.xml提取图片与单元格的关系if (cellImagesXml ! null) {DocumentBuilderFactory factory DocumentBuilderFactory.newInstance();DocumentBuilder builder factory.newDocumentBuilder();Document cellImagesDoc builder.parse(new ByteArrayInputStream(cellImagesXml.getBytes(UTF-8)));NodeList cellImageNodes cellImagesDoc.getElementsByTagName(etc:cellImage);for (int i 0; i cellImageNodes.getLength(); i) {Element cellImageElement (Element) cellImageNodes.item(i);// 获取图片 name 属性如 ID_6C483737A6AC427DAA4E4974252FB8A8Element picElement (Element) cellImageElement.getElementsByTagName(xdr:pic).item(0);Element cNvPr (Element) picElement.getElementsByTagName(xdr:cNvPr).item(0);// e.g., ID_6C483737A6AC427DAA4E4974252FB8A8String imageName cNvPr.getAttribute(name);// 获取 r:embed 属性如 rId1Element blipFill (Element) picElement.getElementsByTagName(xdr:blipFill).item(0);Element blip (Element) blipFill.getElementsByTagName(a:blip).item(0);// e.g., rId1String rId blip.getAttribute(r:embed);// e.g., image1.pngString imageFileName relsMap.get(rId);// TODO: 根据需要确定图片对应的单元格位置// 由于cellimages.xml中没有直接包含单元格位置的信息这里需要通过其他途径获取// 例如可以通过图片的位置信息如x, y坐标与单元格的位置对应// 但是这需要解析更多的XML信息这里假设图片对应的顺序与数据行对应// 保存图片到本地byte[] imageBytes imagesData.get(imageFileName);if (imageBytes ! null) {String savedImagePath outputDir File.separator imageFileName;FileOutputStream fos new FileOutputStream(savedImagePath);fos.write(imageBytes);fos.close();// 由于缺少单元格位置信息这里需要自定义逻辑进行映射// 例如可以将图片顺序与数据行顺序对应// 这里将图片名存储到一个列表中后续与数据行进行关联cellImageMap.put(imageName, savedImagePath);}}}return cellImageMap;}
}
ImportImgDemoApi.java
// ImportImgDemoApi.java
package com.sinosoft.hanlin.jyy.core.api;import com.alibaba.excel.EasyExcel;
import com.sinosoft.hanlin.jyy.annotation.NoAuth;
import com.sinosoft.hanlin.jyy.core.vo.common.WPSExcelImportImgDemoVo;
import com.sinosoft.hanlin.jyy.handler.WPSExcelImportImgExtractor;
import com.sinosoft.hanlin.jyy.handler.WPSExcelImportImgListener;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.List;
import java.util.Map;/*** 数据图片导入*/
Slf4j
RestController
RequestMapping(/easy)
public class ImportImgDemoApi {/*** 从输入流中读取 Excel 文件并解析数据包括图片* author 微信 LHYYH0001** param filePath Excel 文件* return 包含解析后数据的列表*/ApiOperation(value Excel导入带图片demo, notes Excel导入带图片demo, produces MediaType.APPLICATION_JSON_VALUE)PostMapping(value /import, produces MediaType.APPLICATION_JSON_VALUE)NoAuthpublic ListWPSExcelImportImgDemoVo importExcel(RequestParam(filePath) MultipartFile filePath, HttpServletResponse httpServletResponse) throws Exception {// 创建监听器WPSExcelImportImgListener listener new WPSExcelImportImgListener();// 获取文件输入流InputStream inputStreamForData filePath.getInputStream();// 使用 EasyExcel 读取数据EasyExcel.read(inputStreamForData, WPSExcelImportImgDemoVo.class, listener).sheet().doRead();ListWPSExcelImportImgDemoVo dataList listener.getDataList();// 重置输入流以供图片提取InputStream inputStreamForImages filePath.getInputStream();// 提取图片并保存WPSExcelImportImgExtractor extractor new WPSExcelImportImgExtractor();// 修改为您希望保存图片的目录String imageOutputDir /Users/javastarboy/Desktop/京东自营/;MapString, String cellImageMap extractor.extractImages(inputStreamForImages, imageOutputDir);// 遍历数据并关联图片// 假设图片的顺序与数据行的顺序一致int imageIndex 0;for (int i 0; i dataList.size(); i) {WPSExcelImportImgDemoVo vo dataList.get(i);// 获取对应的图片String imagePath null;if (i cellImageMap.size()) {imagePath (String) cellImageMap.values().toArray()[i];// 将图片路径设置到实体类vo.setPicture(imagePath);log.info(图片 {} 已关联到标题 {}, imagePath, vo.getTitle());}}// 关闭输入流inputStreamForImages.close();// 返回数据列表return dataList;}
}
验证一下
请求后excel的图片会存储在 imageOutputDir路径下。
存储逻辑可以根据你的业务逻辑做对应调整上面imageOutputDir只是demo演示 分享不易点赞、关注支持下哦~