EasyExcel 单元格根据图片数量动态设置宽度

在使用 EasyExcel 导出 Excel 时,如果某个单元格是图片内容,且存在多张图片,此时就需要单元格根据图片数量动态设置宽度。

经过自己的研究和实验,导出效果如下:

具体代码如下:

  1. EasyExcel 版本
xml 复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
</dependency>
  1. 定义Excel图片链接转换工具
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.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.IoUtils;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * Excel图片链接转换工具
 *
 * @author 天航星
 * @date 2024-07-05 15:03
 */
public class ExcelImageUrlConverterUtils implements Converter<List<String>> {

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    @Override
    public WriteCellData<?> convertToExcelData(List<String> value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
        
        List<ImageData> imageDataList = new ArrayList<>();
        for (String url : value) {
            try {
                URL imageUrl = new URL(url);
                byte[] bytes = IoUtils.toByteArray(imageUrl.openConnection().getInputStream());
                ImageData imageData = new ImageData();
                imageData.setImage(bytes);
                imageDataList.add(imageData);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        WriteCellData writeCellData = new WriteCellData();
        writeCellData.setImageDataList(imageDataList);
        writeCellData.setType(CellDataTypeEnum.STRING);
        return writeCellData;
    }
}
  1. 定义图片属性
java 复制代码
@ExcelProperty(value = "现场图片(模拟机器人视角)", converter = ExcelImageUrlConverterUtils.class)
private List<String> images;
  1. 定义单元格图片写入拦截器
java 复制代码
import cn.hutool.core.util.ObjUtil;
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.Cell;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.util.Units;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 单元格图片写入拦截器
 * 
 * @author 天航星
 * @date 2024-07-05 15:03
 */
public class ImageCellWriteHandler implements CellWriteHandler {

    private final Map<String,List<ImageData>> imageDataMap = new HashMap<>();

    /**
     * 单元格的图片最大张数(每列的单元格图片张数不确定,单元格宽度需按照张数最多的长度来设置)
     */
    private final AtomicReference<Integer> MAX_IMAGE_SIZE = new AtomicReference<>(0);
    
    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, WriteCellData<?> cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        
        if (isHead) {
            return;
        }
        // 将单元格图片数据复制出来,清空单元格图片数据
        if (!ObjUtil.isEmpty(cellData.getImageDataList())) {
            imageDataMap.put(cell.getRowIndex() + "_" + cell.getColumnIndex(), cellData.getImageDataList());
            cellData.setType(CellDataTypeEnum.EMPTY);
            cellData.setImageDataList(new ArrayList<>());
        }
    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        
        if (isHead || ObjUtil.isEmpty(cellDataList)) {
            return;
        }
        String key = cell.getRowIndex() + "_" + cell.getColumnIndex();
        List<ImageData> imageDataList = imageDataMap.get(key);
        if (ObjUtil.isEmpty(imageDataList)) {
            return;
        }
        if (imageDataList.size() > MAX_IMAGE_SIZE.get()) {
            MAX_IMAGE_SIZE.set(imageDataList.size());
        }
        Sheet sheet = cell.getSheet();
        // 设置单元格行高
        sheet.getRow(cell.getRowIndex()).setHeight((short) 900);
        // 设置单元格列宽(乘多少代表容纳多少张图片)
        sheet.setColumnWidth(cell.getColumnIndex(), MAX_IMAGE_SIZE.get() > 0 ? 3493 * MAX_IMAGE_SIZE.get() : 3493);
        // 插入图片
        for (int i = 0; i < imageDataList.size(); i++) {
            ImageData imageData = imageDataList.get(i);
            if (ObjUtil.isEmpty(imageData)) {
                continue;
            }
            byte[] image = imageData.getImage();
            this.insertImage(sheet, cell, image, i);
        }
        imageDataMap.remove(key);
    }
    
    private void insertImage(Sheet sheet, Cell cell, byte[] pictureData, int i) {
        
        // 图片宽度
        int pictureWidth = Units.pixelToEMU(100);
        int index = sheet.getWorkbook().addPicture(pictureData, HSSFWorkbook.PICTURE_TYPE_PNG);
        Drawing<?> drawing = sheet.getDrawingPatriarch();
        if (ObjUtil.isEmpty(drawing)) {
            drawing = sheet.createDrawingPatriarch();
        }
        CreationHelper helper = sheet.getWorkbook().getCreationHelper();
        ClientAnchor anchor = helper.createClientAnchor();
        // 设置图片在哪个单元格中
        anchor.setCol1(cell.getColumnIndex());
        anchor.setCol2(cell.getColumnIndex());
        anchor.setRow1(cell.getRowIndex());
        anchor.setRow2(cell.getRowIndex() + 1);
        // 设置图片在单元格中的位置
        anchor.setDx1(pictureWidth * i);
        anchor.setDx2(pictureWidth + pictureWidth * i);
        anchor.setDy1(0);
        anchor.setDy2(0);
        // 设置图片可以随着单元格移动
        anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
        drawing.createPicture(anchor, index);
    }
}
  1. 写入时引用
java 复制代码
EasyExcel.write("Excel文件名称.xlsx", DemoData.class).sheet("测试").registerWriteHandler(new ImageCellWriteHandler()).doWrite(new DemoData());

环境:

  • JDK:1.8.0_202
  • SpringBoot:2.7.17
  • EasyExcel:3.3.2
相关推荐
咖啡八杯12 小时前
GoF设计模式——迭代器模式
java·后端·设计模式·迭代器模式
AIGS00112 小时前
企业AI落地的关键认知:向量空间JBoltAI的本体语义平台
java·人工智能·人工智能ai大模型应用
KaMeidebaby13 小时前
卡梅德生物技术快报|抗体亲和力成熟工业化调控新机制:差异性浆细胞增殖工艺优化思路
java·开发语言·人工智能·算法·机器学习·架构·spark
醉城夜风~14 小时前
Java详解经典算法题:接雨水(三种实现方案+原理剖析)
java·开发语言·算法
数智化转型推荐官15 小时前
2026统一身份管理系统五大发展趋势解读
java·运维·微服务
看菜鸡互15 小时前
探索用 SlideML 让大模型生成 PPT 的实验方法
java·运维
程序员张317 小时前
SpringBoot集成BCrypt密码加密库
java·spring boot·后端
闲猫17 小时前
Spring AI Agentic 模式(第1部分):Agent Skills——模块化、可复用的能力
java·人工智能·spring
源图客17 小时前
云途物流API开发-鉴权认证(Java)
java·网络·python
liguojun202518 小时前
篮球馆自动计时收费系统:从规则配置到自动结算的全流程拆解
java·大数据·运维·人工智能·物联网·1024程序员节