LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

EasyExcel 组件 - 带图片导出Office文件

admin
2025年6月28日 22:38 本文热度 31

前言

    最近项目中导出Excel需要带图片导出,看了一位大佬的csdn。文末原文地址。

二、问题分析

关于如何实现带图片导出的功能,在官方文档中有一个简单的说明:

官方文档:图片导出

从官方文档中给的代码示例中可以看出,带图片导出有 6 种方式
@Getter@Setter@EqualsAndHashCode@ContentRowHeight(100)@ColumnWidth(100 / 8)public class ImageDemoData {    private File file;    private InputStream inputStream;    /**     * 如果string类型 必须指定转换器,string默认转换成string     */    @ExcelProperty(converter = StringImageConverter.class)    private String string;    private byte[] byteArray;    /**     * 根据url导出     *     * @since 2.1.1     */    private URL url;
    /**     * 根据文件导出 并设置导出的位置。     *     * @since 3.0.0-beta1     */    private WriteCellData<Void> writeCellDataFile;}

多图片导出

但是如果要实现情景介绍案例中每个单元格需要存放多张图片就不能仅使用官方提供的方案去解决了,通常情况下需要自己写一个拦截器,对单元格中的图片进行处理

  • 图片宽度可自设置,单位为 px

  • 添加像素转换因子,默认为 32 ,如果导入的图片超出或未占满表格,可调整该参数

  • 解决图片遮挡单元格的上边框和右边框的问题


转换器:ImageUrlConverter.java

import com.alibaba.excel.converters.Converter;import com.alibaba.excel.enums.CellDataTypeEnum;import com.alibaba.excel.metadata.GlobalConfiguration;import com.alibaba.excel.metadata.data.ImageData;import com.alibaba.excel.metadata.data.ReadCellData;import com.alibaba.excel.metadata.data.WriteCellData;import com.alibaba.excel.metadata.property.ExcelContentProperty;import com.alibaba.excel.util.IoUtils;import com.alibaba.excel.util.ListUtils;import lombok.extern.slf4j.Slf4j;import org.springframework.util.CollectionUtils;import java.io.InputStream;import java.net.URL;import java.util.List;
@Slf4jpublic class ImageUrlConverter implements Converter<List<URL>> {
    @Override    public Class<?> supportJavaTypeKey() {        return List.class;    }
    @Override    public CellDataTypeEnum supportExcelTypeKey() {        return CellDataTypeEnum.EMPTY;    }
    @Override    public List<URL> convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {        return null;    }
    @Override    public WriteCellData<?> convertToExcelData(List<URL> value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {        // 这里进行对数据实体类URL集合处理        List<ImageData> data = ListUtils.newArrayList();        ImageData imageData;        // for 循环一次读取        for (URL url : value) {            try (InputStream inputStream = url.openStream();) {                byte[] bytes = IoUtils.toByteArray(inputStream);                imageData = new ImageData();                imageData.setImage(bytes);                data.add(imageData);            } catch (Exception e) {                log.error("导出临时记录图片异常:", e);            }        }        WriteCellData<?> cellData = new WriteCellData<>();        if (!CollectionUtils.isEmpty(data)) {            // 图片返回图片列表            cellData.setImageDataList(data);            cellData.setType(CellDataTypeEnum.EMPTY);        } else {            // 没有图片使用汉字表示            cellData.setStringValue("无图");            cellData.setType(CellDataTypeEnum.STRING);        }        return cellData;    }}

单元格拦截器:CustomImageModifyStrategy.java

import com.alibaba.excel.enums.CellDataTypeEnum;import com.alibaba.excel.metadata.Head;import com.alibaba.excel.metadata.data.ImageData;import com.alibaba.excel.metadata.data.WriteCellData;import com.alibaba.excel.write.handler.CellWriteHandler;import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;import com.alibaba.excel.write.metadata.holder.WriteTableHolder;import org.apache.poi.hssf.usermodel.HSSFWorkbook;import org.apache.poi.ss.usermodel.*;import org.apache.poi.util.Units;import org.apache.poi.xssf.usermodel.XSSFDrawing;import org.apache.poi.xssf.usermodel.XSSFPicture;import org.apache.poi.xssf.usermodel.XSSFShape;import org.springframework.util.CollectionUtils;import java.util.List;import java.util.concurrent.CopyOnWriteArrayList;import java.util.concurrent.atomic.AtomicReference;
/** * Excel导出单元格中有图片,图片会进行压缩,缩略,插入单元格 * 注意: *      - 该策略是复制原表格中的图片进行缩放,原图片并没有删除掉,而是将尺寸设置为 0 看不到而已,但是依旧占用空间 *      - 且在多 sheet 的环境下,除第一个 sheet,其余的 sheet 图片会被置 0 * 目前上述问题并没有得到解决,如果在导出数据较多或者存在多个 sheet 的情况下不建议使用 */public class CustomImageModifyStrategy implements CellWriteHandler {
    /**     * 已经处理的Cell     */    private final CopyOnWriteArrayList<String> REPEATS = new CopyOnWriteArrayList<>();
    /**     * 单元格的图片最大张数(每列的单元格图片张数不确定,单元格宽度需按照张数最多的长度来设置)     */    private final AtomicReference<Integer> MAX_IMAGE_SIZE = new AtomicReference<>(0);
    /**     * 标记手动添加的图片,用于排除EasyExcel自动添加的图片     */    private final CopyOnWriteArrayList<Integer> CREATE_PIC_INDEX = new CopyOnWriteArrayList<>();
    /**     * 默认图片宽度(单位像素):60     */    private final static int DEFAULT_IMAGE_WIDTH = 60;
    /**     * 默认像素转换因子:32     */    private final static int DEFAULT_PIXEL_CONVERSION_FACTOR = 32;
    /**     * 图片宽度,单位像素     */    private final int imageWidth;
    /**     * 像素转换因子     */    private final int pixelConversionFactor;
    public CustomImageModifyStrategy() {        this.imageWidth = DEFAULT_IMAGE_WIDTH;        this.pixelConversionFactor = DEFAULT_PIXEL_CONVERSION_FACTOR;    }
    public CustomImageModifyStrategy(int imageWidth, int pixelConversionFactor) {        this.imageWidth = imageWidth;        this.pixelConversionFactor = pixelConversionFactor;    }
    @Override    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
    }
    @Override    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    }
    @Override    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, WriteCellData<?> cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {        //  在数据转换成功后 不是头就把类型设置成空        if (isHead) {            return;        }        //将要插入图片的单元格的type设置为空,下面再填充图片        if (!CollectionUtils.isEmpty(cellData.getImageDataList())) {            cellData.setType(CellDataTypeEnum.EMPTY);        }    }
    @Override    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {        //  在 单元格写入完毕后 ,自己填充图片        if (isHead || CollectionUtils.isEmpty(cellDataList)) {            return;        }        boolean listFlag = false;        Sheet sheet = cell.getSheet();        List<ImageData> imageDataList = cellDataList.get(0).getImageDataList();        if (!CollectionUtils.isEmpty(imageDataList)) {            listFlag = true;        }        if (!listFlag && imageDataList == null) {            return;        }        String key = cell.getRowIndex() + "_" + cell.getColumnIndex();        if (REPEATS.contains(key)) {            return;        }        REPEATS.add(key);        if (imageDataList.size() > MAX_IMAGE_SIZE.get()) {            MAX_IMAGE_SIZE.set(imageDataList.size());        }
        int widthValue =  imageWidth * pixelConversionFactor;        sheet.setColumnWidth(cell.getColumnIndex(), listFlag ? widthValue * MAX_IMAGE_SIZE.get() + pixelConversionFactor : widthValue);
        if (listFlag) {            for (int i = 0; i < imageDataList.size(); i++) {                ImageData imageData = imageDataList.get(i);                if (imageData == null) {                    continue;                }                byte[] image = imageData.getImage();                int index = this.insertImage(sheet, cell, image, i);                CREATE_PIC_INDEX.add(index);            }        } else {            this.insertImage(sheet, cell, imageDataList.get(0).getImage(), 0);        }
        // 清除EasyExcel自动添加的没有格式的图片        XSSFDrawing drawingPatriarch = (XSSFDrawing) sheet.getDrawingPatriarch();        List<XSSFShape> shapes = drawingPatriarch.getShapes();        for (int i = 0; i < shapes.size(); i++) {            XSSFShape shape = shapes.get(i);            if (shape instanceof XSSFPicture && !CREATE_PIC_INDEX.contains(i)) {                CREATE_PIC_INDEX.add(i);                XSSFPicture picture = (XSSFPicture) shape;                // 这里只是将图片的大小设置为 0,所以表格依旧会存放该图片                picture.resize(0);            }        }    }
    /**     * 重新插入一个图片     *     * @param sheet       Excel页面     * @param cell        表格元素     * @param pictureData 图片数据     * @param i           图片顺序     */    public int insertImage(Sheet sheet, Cell cell, byte[] pictureData, int i) {        int picWidth = Units.pixelToEMU(imageWidth);        int index = sheet.getWorkbook().addPicture(pictureData, HSSFWorkbook.PICTURE_TYPE_PNG);        Drawing<?> drawing = sheet.getDrawingPatriarch();        if (drawing == null) {            drawing = sheet.createDrawingPatriarch();        }        CreationHelper helper = sheet.getWorkbook().getCreationHelper();        ClientAnchor anchor = helper.createClientAnchor();        /*         * 设置图片坐标         * 为了不让图片遮挡单元格的上边框和右边框,故 x1、x2、y1 这几个坐标点均向后移动了一个像素点         */        anchor.setDx1(Units.pixelToEMU(1) + picWidth * i);        anchor.setDx2(Units.pixelToEMU(1) + picWidth + picWidth * i);        anchor.setDy1(Units.pixelToEMU(1));        anchor.setDy2(0);        //设置图片位置        int columnIndex = cell.getColumnIndex();        anchor.setCol1(columnIndex);        anchor.setCol2(columnIndex);        int rowIndex = cell.getRowIndex();        anchor.setRow1(rowIndex);        anchor.setRow2(rowIndex + 1);        anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);        drawing.createPicture(anchor, index);        return index;    }
}

输出对象类:SceneProcessInfo.java

@Builder@Data@ExcelIgnoreUnannotated//导出只带有ExcelProperty注解的字段// 头背景设置@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, horizontalAlignment = HorizontalAlignmentEnum.FILL, borderLeft = BorderStyleEnum.THIN, borderTop = BorderStyleEnum.THIN, borderRight = BorderStyleEnum.THIN, borderBottom = BorderStyleEnum.THIN)//标题高度@HeadRowHeight(20)//内容高度@ContentRowHeight(40)//内容居中,左、上、右、下的边框显示@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, borderLeft = BorderStyleEnum.THIN, borderTop = BorderStyleEnum.THIN, borderRight = BorderStyleEnum.THIN, borderBottom = BorderStyleEnum.THIN)public class SceneProcessInfo implements Serializable {
    @Serial    private static final long serialVersionUID = 1L;

    @Schema(description = "ID")    private String id;
    ...
    @Schema(description = "图片")    @ExcelProperty(value = "图片",converter = ImageUrlConverter.class)    @ColumnWidth(50)    private List<URL> dangerimgs;
    
    @Schema(description = "图片")    @ExcelProperty(value = "图片",converter = ImageUrlConverter.class)    @ColumnWidth(50)    private List<URL> correction;
        ...
}

代码示例:

    /**     * 带图片导出:多图片导出     */    @Test    public void exportWithPicture03() {
        // 输出文件路径        String fileName = "demo02.xlsx";
        try {            List<URL> imgList1 = new ArrayList<>();            imgList1.add(new URL(                    "图片网络地址"));            imgList1.add(new URL(                    "图片网络地址"));
            List<URL> imgList2 = new ArrayList<>();            imgList2.add(new URL(                    "图片的网络地址"));
            List<SceneProcessInfo> entityList = new ArrayList<>();            entityList.add(SceneProcessInfo.builder()                    .imgList(imgList1)                    .build());            entityList.add(SceneProcessInfo.builder()                    .imgList(imgList2)                    .build());            entityList.add(SceneProcessInfo.builder()                    .build());
            // 图片列最大图片数            AtomicReference<Integer> maxImageSize = new AtomicReference<>(0);            entityList.forEach(item -> {                // 最大图片数大小                if (!CollectionUtils.isEmpty(item.getImgList()) && item.getImgList().size() > maxImageSize.get()) {                    maxImageSize.set(item.getImgList().size());                }            });            // 导出数据            EasyExcel.write(fileName, StaffInfoEntity.class)                    .autoCloseStream(true)                    // 使用图片处理策略                    .registerWriteHandler(                            // 设置每张图片的宽度为 60px,转换因子为 32                            new CustomImageModifyStrategy(6032))                    .sheet("sheet")                    .doWrite(entityList);        } catch (Exception e) {            System.out.println("导出异常");        }    }

导出结果


原文链接:https://blog.csdn.net/xhmico/article/details/137724425


阅读原文:原文链接


该文章在 2025/7/1 23:36:52 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved