小天管理 发表于 2024年6月25日 发表于 2024年6月25日 在平时的开发中,经常要开发 Excel 的导入导出功能。一般使用 poi 或者 EasyExcel 开发,使用 poi 做 excel 比较复杂,大部分开发都会使用 EasyExcel 因为一行代码就能实现导入和导出的功能。但是 EasyExcel 不支持图片的读的操作,本文操作如何实现图片的读和写的功能。 在 EasyExcel 官网的常见问题可以看到 EasyExcel 是不支持读取图片的功能。 读取图片 poi 读取图片 poi 支持图片的读取,使用 poi 写一个工具类,支持图片的读取,首先添加 maven 依赖, EasyExcel 含有 poi 依赖,无需额外添加 poi 依赖: <!-- easyexcel --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.0.5</version> </dependency> <dependency> <groupId>net.sf.jxls</groupId> <artifactId>jxls-core</artifactId> <version>1.0.6</version> </dependency> 读取图片核心代码如下: Workbook workbook = WorkbookFactory.create(inputStream); // 默认读取第一页 XSSFSheet sheet = (XSSFSheet) workbook.getSheetAt(0); List<POIXMLDocumentPart> documentPartList = sheet.getRelations(); for (POIXMLDocumentPart part : documentPartList) { if (part instanceof XSSFDrawing) { XSSFDrawing drawing = (XSSFDrawing) part; List<XSSFShape> shapes = drawing.getShapes(); for (XSSFShape shape : shapes) { XSSFPicture picture = (XSSFPicture) shape; XSSFClientAnchor anchor = picture.getPreferredSize(); CTMarker marker = anchor.getFrom(); int row = marker.getRow(); int col = marker.getCol(); // 从第 2 行开始 if (row > 0 && row <= size) { PictureData pictureData = picture.getPictureData(); String extension = pictureData.suggestFileExtension(); byte[] bytes = pictureData.getData(); } } } } 读取图片流程: 首先要获取第一页( sheet )数据 workbook.getSheetAt(0) 遍历 sheet.getRelations() 提取 XSSFDrawing ,也就是图片数据。 每一行遍历数据数据,获取 byte 字节流。 可能代码复制在 idea 会提示某些方法不存在,这里就需要核对 poi 版本,上面引用的 EasyExcel 的版本是 3.0.5,里面的 poi 版本是 4.1.2。 封装工具类 通过上面的代码可以获取到图片的字节流,然后对字节流做上传图片或者服务存储图片处理,但是每个读取都写一遍这种方式,代码就比较冗余了。所以就需要将上面代码封装成一个工具类。 比如上传一个文件,需要将数据赋值给两个字段 name 和 imageStr: @ExcelProperty("姓名") private String name; @ExcelProperty(value = "图片") private String imageStr; 首先配置一个 ExcelImageProperty 注解,确定哪列的图片需要赋值给对应的图片字段: @Inherited @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ExcelImageProperty { String[] value() default {""}; /** * 图片在第几列 1 开始 * @return */ int index() default -1; } imageStr 对应第二列,字段上 ExcelImageProperty 注解的 index = 2,上面的实体修改如下: @ExcelProperty("姓名") private String name; @ExcelProperty(value = "图片") @ExcelImageProperty(index = 2) private String imageStr; 写好实体和注解后,再写一个工具类。 @Slf4j public class ExcelReadImageUtil { public static <T> void readImage(InputStream inputStream, List<T> list) { try { Workbook workbook = WorkbookFactory.create(inputStream); // 默认读取第一页 XSSFSheet sheet = (XSSFSheet) workbook.getSheetAt(0); List<POIXMLDocumentPart> documentPartList = sheet.getRelations(); Integer size = list.size(); for (POIXMLDocumentPart part : documentPartList) { if (part instanceof XSSFDrawing) { XSSFDrawing drawing = (XSSFDrawing) part; List<XSSFShape> shapes = drawing.getShapes(); for (XSSFShape shape : shapes) { XSSFPicture picture = (XSSFPicture) shape; XSSFClientAnchor anchor = picture.getPreferredSize(); CTMarker marker = anchor.getFrom(); int row = marker.getRow(); int col = marker.getCol(); // 从第 2 行开始 if (row > 0 && row <= size) { PictureData pictureData = picture.getPictureData(); String extension = pictureData.suggestFileExtension(); byte[] bytes = pictureData.getData(); InputStream imageInputStream = new ByteArrayInputStream(bytes); //String url = iTxCosService.uploadFile(new ByteArrayInputStream(bytes), UUID.randomUUID() + "." + extension); for (int i = 0; i < size; i++) { T item = list.get(i); Class clazz = item.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(ExcelImageProperty.class)) { ExcelImageProperty excelImageProperty = field.getAnnotation(ExcelImageProperty.class); int index = excelImageProperty.index(); if (index == col + 1 && row - 1 == i) { field.setAccessible(true); field.set(item,new String(bytes)); } } } } } } } } } catch (IOException | IllegalAccessException e) { e.printStackTrace(); log.error("read image error {}",e); } } } 传参一个列表,通过获取读取输入流获取到图片,赋值给对应的字段。 此模板是一个列表模版,不支持自定义模板。 使用 poi 读取图片,第二行读取数据,遍历每列数据,符合注解字段就赋值。一般获取到输入流后会上传图片,返回一个地址,这里仅仅就获取字节流,赋值给对应的字段。 使用 EasyExcel 读取非图片数据和工具类读取图片数据: InputStream inputStream = multipartFile.getInputStream(); List<DemoExcelInput> demoExcelInputs = EasyExcelFactory.read(multipartFile.getInputStream()).head(DemoExcelInput.class).sheet().doReadSync(); ExcelReadImageUtil.readImage(inputStream,demoExcelInputs); inputStream 不能重复使用,不然会报错 inputStream close 错误。 更多内容请关注我的公众号:小码 A 梦
已推荐帖子