【excel】easy excel如何导出动态列

动态也有多重含义:本文将描述两种动态场景下的解决方案

场景一:例如表头第一列固定为动物,且必定有第二列,第二列的表头可能为猫 也可能为狗;这是列数固定,列名不固定的场景;

场景二:更复杂的场景则为 第二列可能为猫 可能为狗,第三列可能为熊,也可能没有第三列,甚至可能会有第四列;这是表头数和列数都不固定的场景;

文章目录
场景一:表头名不固定
场景二:表头名和列数都不固定
场景一:表头名不固定
如下dto所示,content字段对应的表头可能会变换,我们只需要在ExcelProperty注解中使用占位符替代

@HeadRowHeight(30)
@ContentRowHeight(20)
@Data
public class EasyExcelDTO {
    @ColumnWidth(30)
    @ExcelProperty("标题")
    private String title;

    @ColumnWidth(30)
    @ExcelProperty("${content}")
    private String content;

}


测试代码:
(其中IoUtil是hutool包的工具类)

    public static void main(String[] args) throws FileNotFoundException {

        File file = new File(("C:\\Users\\10057\\Desktop\\easy_excel.xls"));

        List<EasyExcelDTO> res = new ArrayList<>();
        EasyExcelDTO easyExcelDTO = new EasyExcelDTO();
        Map<String, String> map = new HashMap<>();
        // DTO里面有title字段
//        map.put("title","1");
        // 相当于bean拷贝 (下面这行是cglib里面的代码)
        BeanMap.create(easyExcelDTO).putAll(map);
        System.out.println(easyExcelDTO);

        easyExcelDTO.setContent("666");
        res.add(easyExcelDTO);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("content", "测试动态标题111");
        EasyExcel.write(byteArrayOutputStream, EasyExcelDTO.class).sheet()
                .registerWriteHandler(new ExcelHandler(jsonObject))
                .doWrite(res);

        FileOutputStream fos = new FileOutputStream(file);

        IoUtil.copy(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()), fos);
    }


导出的excel结果示例:可以看到第二列的表头内容成功被替换


场景二:表头名和列数都不固定
实体类代码:
(实际使用中,将DynamicFieldExcelDTO作为一个基类,而涉及了动态导出的DTO类继承该类 ,例如 AnimalExcelDTO extends DynamicFieldExcelDTO, 这么做的目的 主要是需要设定一个dynamicFields字段,用于处理器里面映射并替换值)

dynamicFields的key值则为表头,value为内容值

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;

import java.util.Map;

@HeadRowHeight(30)
@ContentRowHeight(20)
@Data
public class DynamicFieldExcelDTO {

    @ColumnWidth(20)
    @ExcelProperty("订单编号")
    private String orderNo;

    /**
     * K: 表头  V:值 (注意要加上@ExcelIgnore)
     */
    @ExcelIgnore
    private Map<String,Object> dynamicFields;
}


测试代码:


    /**
     *  动态字段导出方法 (不需要调整之前的固定字段)
     * usage: exportDTO类 <b><u>已有固定字段</u></b> ,需要新增动态字段(即需要追加动态表头及对应数据)
     * 模拟数据: orderNo为固定字段,dynamicFields map中的key为动态表头
     * {@link DynamicColumnWriteHandler}
     */
    @Test
    public void dynamicHeadMixWrite() {
        String fileName =  "D:\\test\\out.xlsx";
        List<String> header = Arrays.asList("动态头部1","动态头部2","动态头部3");
        List<DynamicFieldExcelDTO> dataList = new ArrayList<>();
        DynamicFieldExcelDTO d1 = new DynamicFieldExcelDTO();
        d1.setOrderNo("订单111");
        Map<String, Object> map = new HashMap<>();
        map.put("动态头部1","aaa111");
        map.put("动态头部2","aaa222");
        map.put("动态头部3","aaa333");
        d1.setDynamicFields(map);

        DynamicFieldExcelDTO d2 = new DynamicFieldExcelDTO();
        d2.setOrderNo("订单222");
        Map<String, Object> map2 = new HashMap<>();
        map2.put("动态头部1","bbb111");
        map2.put("动态头部2","bbb222");
        map2.put("动态头部3","bbb333");
        d2.setDynamicFields(map2);

        DynamicFieldExcelDTO d3 = new DynamicFieldExcelDTO();
        d3.setOrderNo("订单333");
        Map<String, Object> map3 = new HashMap<>();
        map3.put("动态头部1","ccc111");
        map3.put("动态头部2","ccc222");
        map3.put("动态头部3","ccc333");
        d3.setDynamicFields(map3);

        dataList.add(d1);
        dataList.add(d2);
        dataList.add(d3);

        EasyExcel.write(fileName,DynamicFieldExcelDTO.class)
            .registerWriteHandler(new DynamicColumnWriteHandler<>(header,dataList))
            .sheet()
            .doWrite(dataList);
    }
    
    /**
     * 如果全部为动态字段 将动态数据传参即可
     */
     @Test
    public void dynamicHeadWrite() {
        String fileName =  "D:\\test\\out.xlsx";
        EasyExcel.write(fileName)
            // 这里放入动态头
            .head(new ArrayList<>()).sheet("模板")
            // dataList
            .doWrite(new ArrayList());
    }


处理器代码:
主要逻辑是在afterRowDispose方法里面追加cell,并通过判断列数、是否创建过表头,避免重复添加


/**
 * 动态导出
 * @author csdn:孟秋与你
 */
public class DynamicColumnWriteHandler<T extends DynamicFieldExcelDTO> implements RowWriteHandler {

    private final List<String> dynamicHeaders;
    private final List<T> dataList;
    private ConcurrentHashMap<String,Boolean> isHeaderCreated = new ConcurrentHashMap<>();
    /**
     * 用于记录表头列数
     */
    private AtomicInteger totalHeaderColumns = new AtomicInteger(-1);

    public DynamicColumnWriteHandler(List<String> dynamicHeaders, List<T> dataList) {
        this.dynamicHeaders = dynamicHeaders;
        this.dataList = dataList;
    }

    @Override
    public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, Integer relativeRowIndex, Boolean isHead) { }

    @Override
    public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer rowIndex, Boolean isHead) {}


    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer rowIndex, Boolean isHead) {
        if (isHead && rowIndex == 0) {
            // 获取默认的头部样式
            CellStyle headerCellStyle = row.getCell(0).getCellStyle();
            // 创建动态列头部
            int lastCellIndex = row.getLastCellNum() == -1 ? 0 : row.getLastCellNum();

            // 记录表头的总列数
            totalHeaderColumns.set(lastCellIndex + dynamicHeaders.size());

            for (int i = 0; i < dynamicHeaders.size(); i++) {
                if (Objects.equals(isHeaderCreated.get(dynamicHeaders.get(i)), Boolean.TRUE)) {
                    continue;
                }
                Cell cell = row.createCell(lastCellIndex + i);
                cell.setCellValue(dynamicHeaders.get(i));
                // 设置样式
                cell.setCellStyle(headerCellStyle);
                isHeaderCreated.put(dynamicHeaders.get(i),Boolean.TRUE);

            }
        } else if (!isHead) {

            // 检查数据行是否超出表头的列数
            if (row.getLastCellNum() + dynamicHeaders.size() > totalHeaderColumns.get()) {
                // 如果超出,不写入该行数据
                return;
            }
            // 数据行的写入逻辑
            if (rowIndex < dataList.size()) {
                T dto = dataList.get(rowIndex);
                // k:与表头内容对应
                Map<String, Object> dynamicFields = dto.getDynamicFields();

                int lastCellIndex = row.getLastCellNum() == -1 ? 0 : row.getLastCellNum();
                for (int i = 0; i < dynamicHeaders.size(); i++) {
                    Cell cell = row.createCell(lastCellIndex + i);
                    Object value = dynamicFields.get(dynamicHeaders.get(i));
                    cell.setCellValue(value == null ? "" : value.toString());
                }
            }
        }
    }

}



导出的excel结果示例:可以看到订单编号固定字段,以及其它动态字段 都准确的导出。


其中处理器写的比较仓促,网上用处理器实现动态列的案例很少,博主自己测试通过,如果发现有问题或者更好的建议 欢迎各位在评论区指出;


如果追求稳妥的话,直接在head里面传动态参数即可
(dynamicHeadWrite 方法,可以参考github中easy excel给出的官方demo ),
缺点就是需要将所有的表头都放到headers里面。

假设有个业务场景:你们项目中已有导出近百个字段的报表功能,这些字段之前都是固定的,突然加了个需求 需要导出数个动态字段; 为了这几个动态字段 把之前所有固定字段都写一遍 存入list中 代码可能会很丑陋, 如:

List headers = new ArrayList();
headers.add("第一列");
headers.add("第二列");
// ...
headers.add("第一百列");
// 动态列
List dynamicColumn = xxx;
headers.addAll(dynamicColumn);


String fileName =  "D:\\test\\out.xlsx";
EasyExcel.write(fileName)
    // 这里放入动态头
    .head(headers).sheet("模板")
    // dataList 你的数据
    .doWrite(dataList);
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.youkuaiyun.com/qq_36268103/article/details/143716593

### 使用 Easy Excel 实现固定列加动态列导出功能 在使用 Easy Excel 库时,可以通过定义复杂表头和动态表头的方式实现固定列与动态列的结合导出。以下是具体实现方法: #### 1. 引入依赖 确保在项目的 `pom.xml` 文件中引入了正确的 Easy Excel 依赖: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.7</version> </dependency> ``` 此版本支持复杂表头和动态列的功能[^3]。 #### 2. 定义数据模型 创建一个包含固定列和动态列的数据模型类。例如: ```java public class DynamicData { private String fixedColumn; // 固定列 private Map<String, Object> dynamicColumns; // 动态列 // Getters and Setters } ``` #### 3. 处理复杂表头 通过构建 `List<List<String>>` 的方式定义表头。以下是一个示例代码,展示如何组合固定列和动态列: ```java // 固定列名称 List<String> fixedHeaders = Arrays.asList("固定列"); // 动态列名称 List<String> dynamicHeaders = Arrays.asList("动态列1", "动态列2", "动态列3"); // 构建表头 List<List<String>> head = new ArrayList<>(); head.add(fixedHeaders); head.add(dynamicHeaders); ``` 上述代码将固定列与动态列合并到同一个表头中[^1]。 #### 4. 数据准备 准备需要导出的数据,包括固定列和动态列的内容。例如: ```java List<Map<String, Object>> dataList = new ArrayList<>(); Map<String, Object> dataRow = new LinkedHashMap<>(); dataRow.put("fixedColumn", "固定值"); dataRow.put("dynamicColumn1", "动态值1"); dataRow.put("dynamicColumn2", "动态值2"); dataRow.put("dynamicColumn3", "动态值3"); dataList.add(dataRow); ``` #### 5. 导出 Excel 使用 Easy Excel 提供的 `ExcelWriter` 类进行写入操作。以下是一个完整的导出示例: ```java import com.alibaba.excel.EasyExcel; import com.alibaba.excel.write.metadata.WriteSheet; import java.util.ArrayList; import java.util.List; import java.util.Map; public class EasyExcelDynamicExample { public static void main(String[] args) { // 文件路径 String fileName = "example.xlsx"; // 表头 List<List<String>> head = new ArrayList<>(); head.add(Arrays.asList("固定列", "动态列1", "动态列2", "动态列3")); // 数据 List<Map<String, Object>> data = new ArrayList<>(); Map<String, Object> row = new LinkedHashMap<>(); row.put("固定列", "固定值"); row.put("动态列1", "动态值1"); row.put("动态列2", "动态值2"); row.put("动态列3", "动态值3"); data.add(row); // 写入 Excel EasyExcel.write(fileName).head(head).sheet("Sheet1").doWrite(data); } } ``` 上述代码实现了固定列与动态列的结合导出,并生成了一个 Excel 文件[^2]。 --- ### 注意事项 - 动态列的名称需要在运行时确定,因此通常需要从数据库或其他来源获取。 - 如果涉及多级表头,可以参考官方文档中的 `head()` 方法处理逻辑[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值