首页 > 其他分享 >EasyExcel 无法读取图片?用poi写了一个工具类

EasyExcel 无法读取图片?用poi写了一个工具类

时间:2024-06-23 23:54:39浏览次数:24  
标签:读取 String EasyExcel inputStream poi 图片

在平时的开发中,经常要开发 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 错误。

写图片

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

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

添加写的实体:

@Data
public class DemoExcelInput {

    @ExcelProperty("姓名")
    private String name;

    @ExcelProperty(value = "图片",converter = ExcelUrlImageConverter.class)
    private String imageStr;

    @ExcelProperty("url")
    private URL imageUrl;

    @ExcelProperty("inputstream")
    private InputStream inputStream;

    @ExcelProperty("bytes")
    private byte[] bytes;
}

读取图片

List<DemoExcelInput> demoExcelInputs = new ArrayList<>();
DemoExcelInput demoExcelInput = new DemoExcelInput();
demoExcelInput.setName("aa");
String url = "https://p26-passport.byteacctimg.com/img/user-avatar/82b069ce17bb5b0eccb7ee67d3f6f3bc~180x180.awebp";
demoExcelInput.setImageStr(url);
demoExcelInput.setImageUrl(new URL(url));
demoExcelInputs.add(demoExcelInput);

InputStream inputStream = new URL(url).openStream();
demoExcelInput.setInputStream(inputStream);
byte[] bytes = IoUtils.toByteArray(new URL(url).openStream());
demoExcelInput.setBytes(bytes);

response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName= "导出excel模板";
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
response.setHeader("Content-disposition","attachment;filename*=utf-8''"+encodedFileName+".xlsx");
EasyExcel.write(response.getOutputStream(),DemoExcelInput.class).sheet("模板").doWrite(demoExcelInputs);

导出文件截图:

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

创建 ExcelUrlImageConverter 转换器:

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.IoUtils;
import com.alibaba.excel.util.StringUtils;

import java.io.InputStream;
import java.net.URL;

public class ExcelUrlImageConverter implements Converter<String> {

    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) throws Exception {
        String urlString = context.getValue();
        if (StringUtils.isBlank(urlString)) {
            return new WriteCellData<>("");
        }
        URL url = new URL(urlString);
        InputStream inputStream = url.openStream();
        byte[] bytes = IoUtils.toByteArray(inputStream);
        return new WriteCellData<>(bytes);
    }
}

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

标签:读取,String,EasyExcel,inputStream,poi,图片
From: https://www.cnblogs.com/jeremylai7/p/18264172

相关文章

  • 【java】解决EasyExcel读取InputStream时解析不到excel文件类型的问题
    正常利用EasyExcel读取excel中sheet时会调用如下接口:publicstaticExcelReaderBuilderread(StringpathName,ReadListenerreadListener){returnread((String)pathName,(Class)null,readListener);}publicstaticExcelReaderBuilderread(Stringp......
  • 【java-POI】如何将一个WorkBook转为一个InputStream?
    /***利用workBook创建一个输入流用于后续操作**@return*/privateInputStreamcreateInputSream(){if(inputStream!=null){try{inputStream.reset();returninputStream;......
  • 通过ESP32读取I2C温湿度传感器项目:协议与代码实例
    简介在本项目中,我们将使用ESP32开发板读取I2C温湿度传感器的数据。我们将详细介绍I2C协议,并提供图文并茂的代码实例,帮助你快速上手。项目流程选择硬件:ESP32开发板、I2C温湿度传感器(如DHT12、HTU21D、SHT30等)、连接线和面包板。了解I2C协议:I2C(Inter-IntegratedCircuit)是......
  • 【测试文章搬运】用selenium并从excel里面读取测试数据——采用数据驱动测试自动化框
    SeleniumFrameworkCreationAndAccessingTestDataFromExcel–SeleniumTutorial在本系列的当前教程中,我们将为您提供一个示例框架,用于存储测试数据及其Excel操作的Excel。同样,我们将继续前进并引入新的策略和资源来完善我们的框架。让我们学习一下:使用示例项目的......
  • [题解]CF988D Points and Powers of Two
    思路首先发现选出的数最多\(3\)个,考虑反证法。假设选出了四个数\(a,b,c,d\),并令:\[|a-b|=2^{x_1},|b-c|=2^{x_2},|c-d|=2^{x_3}\]又因为,\(|a-c|,|b-d|\)也都是\(2\)的次幂,那么有\(x_1=x_2=x_3\)。于是\(|a-d|=3\times2^{x_0}\neq2^k\)。在......
  • 269:vue+openlayers 利用 MultiPoint 显示多点
    作者:还是大剑师兰特,曾为美国某知名大学计算机专业研究生,现为国内GIS领域高级前端工程师,CSDN知名博主,深耕openlayers、leaflet、mapbox、cesium,canvas,echarts等技术开发,欢迎加微信(gis-dajianshi),一起交流。查看本专栏目录-本文是第269个示例文章目录一......
  • 大模型基本概念学习 - Checkpoint、PyTorch、 TensorFlow、Transformers、ModelScope
    文章目录前言一、checkpoint二、TensorFlow1.简介2.主要特点3.示例代码三、PyTorch1.简介2.主要特点3.示例代码四、TensorFlow和PyTorch区别五、Transformers六、Transformers通过配置或自动检测来决定使用PyTorch或TensorFlow1.自动检测2.通过环境变量配......
  • windows powershell 如何读取大文件前10行
    在WindowsPowerShell中,可以使用Get-Contentcmdlet来读取文件内容。对于大文件,直接使用Get-Content会加载整个文件,这可能会导致性能问题或内存溢出。为了避免这样的问题,我们可以通过指定读取的行数来获取文件的前几行。以下是一些常见的方法来读取大文件的前10行:Get-Cont......
  • 【笔记】表格处理(一)Apache POI
    表格处理ApachePOI表格处理一、简介HSSF和XSSF有啥不同?二、使用步骤(一)依赖(二)基础使用示例1.创建一个简单的Excel文件2.读取一个Excel文件3.设置单元格样式4.合并单元格5.添加图片6.数据有效性和下拉列表7.自动调整列宽8.公式计算9.日期和时间格式10.......
  • HC32L130读取SD3078时间
    一.SD3078电路图二.HC32L130IO模拟IIC 1.app_i2c_gpio.h/*****************************************************************************//**\fileapp_i2c_gpio.h****Headerfileforlcdfunctions******History:**-2024-06-21马天义微信:......