经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
EasyExcel 无法读取图片?用poi写了一个工具类
来源:cnblogs  作者:小码A梦  时间:2024/6/25 8:53:55  对本文有异议

在平时的开发中,经常要开发 Excel 的导入导出功能。一般使用 poi 或者 EasyExcel 开发,使用 poi 做 excel 比较复杂,大部分开发都会使用 EasyExcel 因为一行代码就能实现导入和导出的功能。但是 EasyExcel 不支持图片的读的操作,本文操作如何实现图片的读和写的功能。

在 EasyExcel 官网的常见问题可以看到 EasyExcel 是不支持读取图片的功能。

读取图片

poi 读取图片

poi 支持图片的读取,使用 poi 写一个工具类,支持图片的读取,首先添加 maven 依赖, EasyExcel 含有 poi 依赖,无需额外添加 poi 依赖:

  1. <!-- easyexcel -->
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>easyexcel</artifactId>
  5. <version>3.0.5</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>net.sf.jxls</groupId>
  9. <artifactId>jxls-core</artifactId>
  10. <version>1.0.6</version>
  11. </dependency>

读取图片核心代码如下:

  1. Workbook workbook = WorkbookFactory.create(inputStream);
  2. // 默认读取第一页
  3. XSSFSheet sheet = (XSSFSheet) workbook.getSheetAt(0);
  4. List<POIXMLDocumentPart> documentPartList = sheet.getRelations();
  5. for (POIXMLDocumentPart part : documentPartList) {
  6. if (part instanceof XSSFDrawing) {
  7. XSSFDrawing drawing = (XSSFDrawing) part;
  8. List<XSSFShape> shapes = drawing.getShapes();
  9. for (XSSFShape shape : shapes) {
  10. XSSFPicture picture = (XSSFPicture) shape;
  11. XSSFClientAnchor anchor = picture.getPreferredSize();
  12. CTMarker marker = anchor.getFrom();
  13. int row = marker.getRow();
  14. int col = marker.getCol();
  15. // 从第2行开始
  16. if (row > 0 && row <= size) {
  17. PictureData pictureData = picture.getPictureData();
  18. String extension = pictureData.suggestFileExtension();
  19. byte[] bytes = pictureData.getData();
  20. }
  21. }
  22. }
  23. }

读取图片流程:

  • 首先要获取第一页(sheet)数据 workbook.getSheetAt(0)
  • 遍历 sheet.getRelations() 提取 XSSFDrawing,也就是图片数据。
  • 每一行遍历数据数据,获取 byte 字节流。

可能代码复制在 idea 会提示某些方法不存在,这里就需要核对 poi 版本,上面引用的 EasyExcel 的版本是 3.0.5,里面的 poi 版本是 4.1.2

封装工具类

通过上面的代码可以获取到图片的字节流,然后对字节流做上传图片或者服务存储图片处理,但是每个读取都写一遍这种方式,代码就比较冗余了。所以就需要将上面代码封装成一个工具类。

比如上传一个文件,需要将数据赋值给两个字段 name 和 imageStr:

  1. @ExcelProperty("姓名")
  2. private String name;
  3. @ExcelProperty(value = "图片")
  4. private String imageStr;

首先配置一个 ExcelImageProperty 注解,确定哪列的图片需要赋值给对应的图片字段

  1. @Inherited
  2. @Target({ElementType.FIELD})
  3. @Retention(RetentionPolicy.RUNTIME)
  4. public @interface ExcelImageProperty {
  5. String[] value() default {""};
  6. /**
  7. * 图片在第几列 1开始
  8. * @return
  9. */
  10. int index() default -1;
  11. }

imageStr 对应第二列,字段上 ExcelImageProperty 注解的 index = 2,上面的实体修改如下:

  1. @ExcelProperty("姓名")
  2. private String name;
  3. @ExcelProperty(value = "图片")
  4. @ExcelImageProperty(index = 2)
  5. private String imageStr;

写好实体和注解后,再写一个工具类。

  1. @Slf4j
  2. public class ExcelReadImageUtil {
  3. public static <T> void readImage(InputStream inputStream, List<T> list) {
  4. try {
  5. Workbook workbook = WorkbookFactory.create(inputStream);
  6. // 默认读取第一页
  7. XSSFSheet sheet = (XSSFSheet) workbook.getSheetAt(0);
  8. List<POIXMLDocumentPart> documentPartList = sheet.getRelations();
  9. Integer size = list.size();
  10. for (POIXMLDocumentPart part : documentPartList) {
  11. if (part instanceof XSSFDrawing) {
  12. XSSFDrawing drawing = (XSSFDrawing) part;
  13. List<XSSFShape> shapes = drawing.getShapes();
  14. for (XSSFShape shape : shapes) {
  15. XSSFPicture picture = (XSSFPicture) shape;
  16. XSSFClientAnchor anchor = picture.getPreferredSize();
  17. CTMarker marker = anchor.getFrom();
  18. int row = marker.getRow();
  19. int col = marker.getCol();
  20. // 从第2行开始
  21. if (row > 0 && row <= size) {
  22. PictureData pictureData = picture.getPictureData();
  23. String extension = pictureData.suggestFileExtension();
  24. byte[] bytes = pictureData.getData();
  25. InputStream imageInputStream = new ByteArrayInputStream(bytes);
  26. //String url = iTxCosService.uploadFile(new ByteArrayInputStream(bytes), UUID.randomUUID() + "." + extension);
  27. for (int i = 0; i < size; i++) {
  28. T item = list.get(i);
  29. Class clazz = item.getClass();
  30. Field[] fields = clazz.getDeclaredFields();
  31. for (Field field : fields) {
  32. if (field.isAnnotationPresent(ExcelImageProperty.class)) {
  33. ExcelImageProperty excelImageProperty = field.getAnnotation(ExcelImageProperty.class);
  34. int index = excelImageProperty.index();
  35. if (index == col + 1 && row - 1 == i) {
  36. field.setAccessible(true);
  37. field.set(item,new String(bytes));
  38. }
  39. }
  40. }
  41. }
  42. }
  43. }
  44. }
  45. }
  46. } catch (IOException | IllegalAccessException e) {
  47. e.printStackTrace();
  48. log.error("read image error {}",e);
  49. }
  50. }
  51. }

传参一个列表,通过获取读取输入流获取到图片,赋值给对应的字段。

  • 此模板是一个列表模版,不支持自定义模板。
  • 使用 poi 读取图片,第二行读取数据,遍历每列数据,符合注解字段就赋值。一般获取到输入流后会上传图片,返回一个地址,这里仅仅就获取字节流,赋值给对应的字段。

使用 EasyExcel 读取非图片数据和工具类读取图片数据:

  1. InputStream inputStream = multipartFile.getInputStream();
  2. List<DemoExcelInput> demoExcelInputs = EasyExcelFactory.read(multipartFile.getInputStream()).head(DemoExcelInput.class).sheet().doReadSync();
  3. ExcelReadImageUtil.readImage(inputStream,demoExcelInputs);

inputStream 不能重复使用,不然会报错 inputStream close 错误。

写图片

EasyExcel 支持多种格式的写图片,包括:

  • URL
  • InputStream
  • byte[]
  • File
  • 自定义转换器

添加写的实体:

  1. @Data
  2. public class DemoExcelInput {
  3. @ExcelProperty("姓名")
  4. private String name;
  5. @ExcelProperty(value = "图片"converter = ExcelUrlImageConverter.class)
  6. private String imageStr;
  7. @ExcelProperty("url")
  8. private URL imageUrl;
  9. @ExcelProperty("inputstream")
  10. private InputStream inputStream;
  11. @ExcelProperty("bytes")
  12. private byte[] bytes;
  13. }

读取图片

  1. List<DemoExcelInput> demoExcelInputs = new ArrayList<>();
  2. DemoExcelInput demoExcelInput = new DemoExcelInput();
  3. demoExcelInput.setName("aa");
  4. String url = "https://p26-passport.byteacctimg.com/img/user-avatar/82b069ce17bb5b0eccb7ee67d3f6f3bc~180x180.awebp";
  5. demoExcelInput.setImageStr(url);
  6. demoExcelInput.setImageUrl(new URL(url));
  7. demoExcelInputs.add(demoExcelInput);
  8. InputStream inputStream = new URL(url).openStream();
  9. demoExcelInput.setInputStream(inputStream);
  10. byte[] bytes = IoUtils.toByteArray(new URL(url).openStream());
  11. demoExcelInput.setBytes(bytes);
  12. response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  13. response.setCharacterEncoding("utf-8");
  14. // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
  15. String fileName= "导出excel模板";
  16. String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
  17. response.setHeader("Content-disposition","attachment;filename*=utf-8''"+encodedFileName+".xlsx");
  18. EasyExcel.write(response.getOutputStream(),DemoExcelInput.class).sheet("模板").doWrite(demoExcelInputs);

导出文件截图:

但是上面的 imageStr 对应的 String 类型 EasyExcel 并不支持,但是却能导出图片,这就需要使用到自定义转换器

创建 ExcelUrlImageConverter 转换器:

  1. import com.alibaba.excel.converters.Converter;
  2. import com.alibaba.excel.converters.WriteConverterContext;
  3. import com.alibaba.excel.metadata.data.WriteCellData;
  4. import com.alibaba.excel.util.IoUtils;
  5. import com.alibaba.excel.util.StringUtils;
  6. import java.io.InputStream;
  7. import java.net.URL;
  8. public class ExcelUrlImageConverter implements Converter<String> {
  9. @Override
  10. public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) throws Exception {
  11. String urlString = context.getValue();
  12. if (StringUtils.isBlank(urlString)) {
  13. return new WriteCellData<>("");
  14. }
  15. URL url = new URL(urlString);
  16. InputStream inputStream = url.openStream();
  17. byte[] bytes = IoUtils.toByteArray(inputStream);
  18. return new WriteCellData<>(bytes);
  19. }
  20. }

将读取到图片流转到对象 WriteCellData 中,就能写图片了。

原文链接:https://www.cnblogs.com/jeremylai7/p/18264172

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号