EasyExcel导出动态表头并且绑定数据的解决过程

概要

关于execl静态表头+动态表头的一些想法和实践。由于项目中需要导出execl的静态表头+动态表头,于是便记录了遇见的问题和一些解决问题的思路。

execl版本

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>easyexcel</artifactId>
	<version>2.2.7</version>
</dependency>

easyexcel官网对动态表头的案例

easyexcel官网动态表头例子

在这里插入图片描述
把其中最重要的部分标红了,动态表头和数据是分开写的。
思考1: 动态表头写入和数据如何映射正确并写入正确。
思考2: 业务场景中是否存在导出的字段是横表和竖表。如果为竖表导出的业务场景可能更复杂,表头和数据的映射也为更复杂。

ps: 横表和竖表的区别简单解释下

横表
+----+-------+-------+
| id | col1  | col2  |
+----+-------+-------+
| 1  | val1  | val2  |
| 2  | val3  | val4  |
+----+-------+-------+
竖表
+----+-------+-----+
| id | attr  | val |
+----+-------+-----+
| 1  | col1  | val1|
| 2  | col2  | val2|
| 3  | col1  | val3|
| 4  | col2  | val4|
+----+-------+-----+

业务场景分析

目前是以竖表的场景来解决的,因为竖表可以包含横表的方式
先写入表头的部分,后写入数据。相当于需要在代码逻辑处理好映射关系,保证表头和数据能够映射上,并且不会错位。上图
在这里插入图片描述
案例1
如果表头1、表头2、表头3…表头8,其中表头1、表头2、表头3、表头4为静态表头、后面的为动态表头(只是表头名称的变更)
案例2
如果表头1、表头2、表头3…表头8,其中表头1、表头2、表头3、表头4为静态表头、后面的为动态表头(不只是表头名称的变更,表头的数量随表字段变化而变化)

技术解决方案

解决思路:

  • 可以将静态表头和动态表头都作为动态表头写入,并且保证写入顺序。
  • 写入数据的时候,需要根据表头的写入顺序和数据一一映射。
  • 如果导出存在多条记录,每一条对应的动态表头数据都不一致,比如说第1条记录静态表头+动态表头一共有30个字段,第2条记录静态表头+动态表头一共有40个字段,第3条记录静态表头+动态表头一共有45个字段,第4条记录静态表头+动态表头一共有50个字段,如果存在这种复杂的场景就需要先把动态表头的部分全部先取并集,再和静态表头拼接起来,这样表头的部分才是完整的,但是也会存在表头和数据错位的问题,这种解决方式就需要动态扩容,举个例子:所有数据动态表头部分取完并集后长度为30,加上静态表头20,一共就是50,第一条记录静态20+动态10=30,第二条记录静态20+动态20=并集40,第三条记录静态20+动态25=并集45,这个时候数据长度也不一样,就容易错位如图
    在这里插入图片描述
  • 写下表头和数据映射的核心代码,因为其他代码为组装业务数据没有参考意义

    private static List<List<Object>> data(List<String> headList, List<List<ExportField>> exportDataList) {
        Map<String, Integer> haedOrderMap = Maps.newLinkedHashMap();
        IntStream.range(0, headList.size()).forEachOrdered(i -> {
            String head = headList.get(i);
            haedOrderMap.put(head, i);
        });

        List<List<ExportField>> orderData = Lists.newLinkedList();
        // 每列
        for (List<ExportField> fields : exportDataList) {
            growList(headList.size(), fields);
            List<ExportField> orderList = Lists.newLinkedList();
            // 每行
            IntStream.range(0, headList.size()).forEachOrdered(dataIndex -> {
                ExportField export = fields.get(dataIndex);
                Integer headIndex = haedOrderMap.getOrDefault(export.getPropertyName(), null);
                boolean eq = Objects.equals(headIndex, dataIndex);
                if (!eq) {
                    ExportField exportField = new ExportField();
                    exportField.setSort(dataIndex);
                    exportField.setFieldValue("");
                    orderList.add(exportField);
                } else {
                    export.setSort(headIndex);
                    orderList.add(export);
                }
            });
            orderData.add(orderList);
        }

        return orderData.stream()
                .map(m -> m.stream().map(ExportField::getFieldValue).collect(Collectors.toList()))
                .collect(Collectors.toList());
    }

    private static void growList(int headListSize, List<ExportField> fields) {
        int fieldSize = fields.size();
        if (fieldSize < headListSize) {
            IntStream.range(fieldSize, headListSize).mapToObj(i -> {
                ExportField exportField = new ExportField();
                exportField.setSort(-1);
                return Lists.newArrayList(exportField);
            }).forEachOrdered(fields::addAll);
        }
    }

@Data
class ExportField {
    private Long bizId;
    private Long fieldId;
    private String fieldName;
    private Object fieldValue;
    private int sort;
    private String propertyName;
}
  • execl字体大小、样式的配置等等等
	
public static void main(String[] args) {
    EasyExcelFactory.write("D:\\tmp\\" + "测试动态表头" + System.currentTimeMillis() + ".xlsx")
            .registerWriteHandler(EasyExcelConfig.setConfigure())
            .registerWriteHandler(new CustomRowHeightStrategy())
            .registerWriteHandler(new CustomCellWidthStrategy())
            .registerWriteHandler(new CustomSheetWriteHandler())
            .head(head())
            .sheet("sheet1")
            .doWrite(data(Lists.newArrayList(), Lists.newArrayList()));
}



public class EasyExcelConfig {

    // 配置字体,表头背景等
    public static HorizontalCellStyleStrategy setConfigure() {
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE1.getIndex());
        WriteFont headWriteFont = new WriteFont();
        // 加粗
        headWriteFont.setBold(true);
        headWriteFont.setFontHeightInPoints((short) 10);
        headWriteCellStyle.setWriteFont(headWriteFont);
        headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        // 设置单元格上边框为细线
        headWriteCellStyle.setBorderTop(BorderStyle.THIN);
        // 设置单元格右边框为细线
        headWriteCellStyle.setBorderRight(BorderStyle.THIN);
        // 设置单元格下边框为细线
        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        return new HorizontalCellStyleStrategy(headWriteCellStyle, getWriteCellStyle());
    }

   private static WriteCellStyle getWriteCellStyle() {
       WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
       // 字体策略
       WriteFont contentWriteFont = new WriteFont();
       // 字体大小
       contentWriteFont.setFontHeightInPoints((short) 8);
       contentWriteCellStyle.setWriteFont(contentWriteFont);
       // 导出数据垂直居中
       contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
       // 导出数据水平居中
       contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
       contentWriteCellStyle.setBorderLeft(BorderStyle.NONE);
       contentWriteCellStyle.setBorderTop(BorderStyle.NONE);
       contentWriteCellStyle.setBorderRight(BorderStyle.NONE);
       contentWriteCellStyle.setBorderBottom(BorderStyle.NONE);
       return contentWriteCellStyle;
   }
}

/**
 * 列宽
 */
public class CustomCellWidthStrategy extends AbstractHeadColumnWidthStyleStrategy {

    /**
     * 设置每一列的列宽
     */
    @Override
    protected Integer columnWidth(Head head, Integer columnIndex) {
        return 18;
    }
}


/**
 * 行高
 */
public class CustomRowHeightStrategy extends AbstractRowHeightStyleStrategy {

    /**
     * 设置表头的行高
     */
    @Override
    protected void setHeadColumnHeight(Row row, int relativeRowIndex) {
        // 默认表头高度
        row.setHeightInPoints(50);
    }

    /**
     * 设置内容的行高
     */
    @Override
    protected void setContentColumnHeight(Row row, int relativeRowIndex) {
        // 默认主体的高度
        row.setHeightInPoints(30);
    }
}


/**
 * 行配置
 */
public class CustomSheetWriteHandler implements SheetWriteHandler {

    public int colSplit = 0, rowSplit = 1, leftmostColumn = 0, topRow = 1;

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        // 得到Sheet
        Sheet sheet = writeSheetHolder.getSheet();
        // 设置冻结窗格第一行
        sheet.createFreezePane(colSplit, rowSplit, leftmostColumn, topRow);
    }
}

小结

如果需要完整的例子可留言有时间整个demo。
到这里就结束啦,祝大家生活愉快!

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
EasyExcel是一个方便、易用、高效的Java Excel操作库,广泛应用于各种Java应用程序中。在EasyExcel中,导出动态表头是常见的操作需求,因为有时候表头的列数和列名是不确定的,需要在运行时动态生成。下面将介绍如何使用EasyExcel导出动态表头。 首先需要定义Excel的列头信息,可以通过继承AbstractWriteHandler接口,重写afterSheetCreate方法,在该方法中动态生成列头信息。代码示例如下: ``` public class DynamicHeadExcelHandler implements AbstractWriteHandler { private List<List<String>> headTitles = new ArrayList<>(); @Override public void afterSheetCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, RowWriteHandler rowWriteHandler) { List<String> headTitle1 = new ArrayList<>(); headTitle1.add("姓名"); headTitle1.add("年龄"); headTitles.add(headTitle1); List<String> headTitle2 = new ArrayList<>(); headTitle2.add("性别"); headTitle2.add("地址"); headTitles.add(headTitle2); writeHead(writeSheetHolder, writeTableHolder); } private void writeHead(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder) { int maxColumnSize = getMaxColumnSize(headTitles); Sheet sheet = writeSheetHolder.getSheet(); sheet.createRow(0); for (int i = 0; i < maxColumnSize; i++) { Cell cell = sheet.getRow(0).createCell(i); cell.setCellValue(getCellValue(i)); cell.setCellStyle(getHeadStyle(writeSheetHolder, writeTableHolder)); } CellRangeAddress range = new CellRangeAddress(0, 0, 0, maxColumnSize - 1); sheet.addMergedRegion(range); } private int getMaxColumnSize(List<List<String>> headTitles) { int max = 0; for (List<String> titles : headTitles) { int size = titles.size(); if (size > max) { max = size; } } return max; } private String getCellValue(int index) { StringBuilder builder = new StringBuilder(); for (List<String> titles : headTitles) { if (titles.size() > index) { builder.append(titles.get(index)).append(" "); } } return builder.toString(); } private CellStyle getHeadStyle(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder) { CellStyle cellStyle = writeSheetHolder.getSheet().getWorkbook().createCellStyle(); Font font = writeSheetHolder.getSheet().getWorkbook().createFont(); font.setBold(true); font.setFontHeightInPoints((short) 14); cellStyle.setFont(font); cellStyle.setAlignment(HorizontalAlignment.CENTER); return cellStyle; } } ``` 在上述代码中,我们定义了两行表头信息,每一行都是一个List<String>类型的数据。在afterSheetCreate方法中,我们通过遍历headTitles来动态生成列头信息。然后在writeHead方法中,我们生成了一个合并单元格的行并将列头信息填入其中。 接下来,我们在程序中使用该ExcelHandler,可以通过以下代码实现: ``` ExcelWriter excelWriter = EasyExcel.write(outputStream).registerWriteHandler(new DynamicHeadExcelHandler()).build(); excelWriter.write(data, EasyExcel.writerSheet().build()); excelWriter.finish(); ``` 在上述代码中,我们创建了一个ExcelWriter对象,并将DynamicHeadExcelHandler注册为WriteHandler。然后我们调用write方法将数据写入Excel中。 综上所述,EasyExcel提供了方便的API来实现动态表头导出,通过继承AbstractWriteHandler接口来实现表头生成。以上就是使用EasyExcel导出动态表头的方法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值