前言

最近的excel导出需求,表头不是固定的,是根据前端的传参,确定要展示的文字和列数。

项目之前用 jxl 的库,先做一个excel模板放在项目文件夹,模板里面有通配字符,代码读取该模板并填充。

这次的需求虽然类似动态表头,但是实现的难度变大了,既有横向的日期分布,又有纵向的信息分类,横向的列数取决于前端参数,用固定模板甚至不可能实现(不可事先预知横向有多少列)。

评估之后很果断地改用EaxyExcel。

改用EaxyExcel,第一不用预定excel模板,第二能灵活设置表头的列数,颜色、宽度。

后来有另一个需求,用EaxyExcel很好的满足,如果用别的库实现起来巨麻烦,等一下再说。

效果图

默认

java easypoiutil多级表头 java excel 动态复杂表头_List

纯白

java easypoiutil多级表头 java excel 动态复杂表头_spring_02

设置背景色

java easypoiutil多级表头 java excel 动态复杂表头_背景色_03

实现

导入依赖

先引入EasyExcel和Guava工具类,还有lombok

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

		<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>30.1-jre</version>
        </dependency>

		<!-- springboot test启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

单元测试

/**
 * 动态表头,并设置宽度,字体,背景
 *
 * @date 01/22/2021 03:36
 */
public class Demo3ForCSDN {

    /**
     * @throws IOException
     */
    @Test
    public void test() throws IOException {

        // 文件输出位置
        OutputStream out = new FileOutputStream("/Users/quanlinglong/Downloads/mergeDemo/demo" + System.currentTimeMillis() + ".xlsx");
        ExcelWriter writer = EasyExcelFactory
                .write(out)
                // 核心代码:表头和正文的样式在此
                .registerWriteHandler(setConfigure())
                .build();

        // 动态添加表头,适用一些表头动态变化的场景
        WriteSheet sheet1 = new WriteSheet();
        sheet1.setSheetName("客户信息");
        sheet1.setSheetNo(0);

        // 创建一个表格,用于 Sheet 中使用
        WriteTable table = new WriteTable();
        table.setTableNo(1);
        // 核心代码:设置表头
        table.setHead(head());
        // 写数据
        writer.write(contentData(), sheet1, table);
        writer.finish();
        out.close();
    }

    private static List<List<String>> head() {
        List<List<String>> headTitles = Lists.newArrayList();
        String empty = " ";
        //第一列,1/2/3行
        headTitles.add(Lists.newArrayList(empty, "交通"));
        //第二列,1/2/3行
        headTitles.add(Lists.newArrayList(empty, "人口数"));
        // 第三列
        headTitles.add(Lists.newArrayList(empty, empty, "频道"));
        // 第四列及之后
        ArrayList<String> foodList = Lists.newArrayList("气温", "风力", "湿度");
        ArrayList<String> mealList = Lists.newArrayList("深圳", "北京", "上海");
        ArrayList<String> dateList = Lists.newArrayList("星期一(2021-01-25)", "星期二(2021-01-26)");
        // 根据实际需要,决定要渲染多少列
        dateList.forEach(date -> {
            mealList.forEach(meal -> {
                foodList.forEach(food -> {
                    headTitles.add(Lists.newArrayList(empty, date, meal, food));
                });
            });
        });
        return headTitles;
    }

    private static List<List<Object>> contentData() {
        List<List<Object>> contentList = Lists.newArrayList();
        //这里一个List<Object>才代表一行数据,需要映射成每行数据填充,横向填充(把实体数据的字段设置成一个List<Object>)
        contentList.add(Lists.newArrayList("骑车", "10", "苹果🍎", "25", "2级", "40%"));
        contentList.add(Lists.newArrayList("高铁", "20", "橙子🍊", "25", "2级", "40%"));
        return contentList;
    }

    //配置字体,表头背景等
    private static HorizontalCellStyleStrategy setConfigure() {
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.YELLOW1.getIndex()); // 黄色背景
        WriteFont headWriteFont = new WriteFont();
        // 加粗
        headWriteFont.setBold(true);
//        headWriteFont.setFontHeightInPoints((short) 14); // 设置行高,不重要
        headWriteCellStyle.setWriteFont(headWriteFont);


        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 字体策略
        WriteFont contentWriteFont = new WriteFont();
        // 字体大小
//        contentWriteFont.setFontHeightInPoints((short) 14);
        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);

        //设置 自动换行
        contentWriteCellStyle.setWrapped(true);
        //设置
        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }
}

根据传进来的参数,决定展示多少列,展示什么内容。写数据就传入一个List,相当于二维数组。官方文档有个例子,定义一个类,用注解定义表头,那样表头的内容是固定的,展示多少列取决于类里面有多少字段,一开始就固定了,不满足我的需求。

java easypoiutil多级表头 java excel 动态复杂表头_spring_04

动态生成表头的方式,不需要定义类,不用先做一个excel模板。后来有一个需求,提供错误重下载的功能,即系统识别到用户上传的excel内容填写有误,要高亮单元格,以及重新下载和填写。这个功能,就把读数据时的表头和内容整理下,用上述方式生成一个流,存到云存储,返回一个链接。

总结一下:

  1. 根据传参决定展示表头的列数和内容,宽度,背景色等
  2. 写数据时传入一个List即可

希望对你有用