前言
最近项目有导出excel报表的需求,以前接触过的excel 报表需求都是使用 原生 poi 和hutool 工具类实现的,项目中 要求使用easyExcel 实现。附上 官网链接:EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel
1 动态表头的需求
1.1 固定项中的动态表头
虽然表头项是动态的,但是表头均出自同一张表或者同一实体类。诸如:有学生类,有 姓名、年龄、性别 、年级、班级 等信息。但是只需要 姓名、年龄 和 年级信息。
这一类情况是最简单的 。可以直接 参照使用 官网上的栗子 来解决@ExcelProperty(value = "字符串标题", index = 0)
实体类配置
package com.example.demo.easyexcelquickstart.entity;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Builder;
import lombok.Data;
/**
* <p>
* Title: Student
* </p>
* <p>
* Description:
* </p>
* <p>
* Copyright: Copyright (c) 2022
* </p>
*
* @author: cw
* @Date: 2023-07-17 11:35
*/
@Data
@Builder
public class Student1 {
@ExcelProperty(value = "姓名", index = 0)
private String name;
@ExcelProperty(value = "年龄", index = 1)
private Integer age;
// 忽略这个字段
@ExcelIgnore
private String sex;
@ExcelProperty(value = "年级", index = 2)
private String grade;
// 忽略这个字段
@ExcelIgnore
private String stuClass;
}
写excel 代码配置
@PostMapping(value = "/test1")
public JsonResponse test1() {
String fileName = filePath + File.separator + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写
try (ExcelWriter excelWriter = EasyExcel.write(fileName, Student1.class).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
excelWriter.write(data(), writeSheet);
}
return JsonResponseFactory.success();
}
效果如下: 只导出了 姓名 年龄和年级信息
1.2 表头不固定需要从数据库中动态获取
此处的数据库的动态获取,指的是 表头可能会有包含多张表(实体类的)信息,诸如:学校信息,课程信息 等。实现无法确定需要导出哪些列(需求中,经常会有此类问题)
例如:我们的excel 报表有3部分组成 :学校信息:学校名称、学校地址、学校电话,学生信息:学生名称、学生年龄、学生年级、学生班级信息和 课程信息 :课程名称、课程类型和课程编码
因为表头的组成来源于 不同的表,实体类(表)无法用一个某一个类来表示。
此问题的解决思路大体分为2步:
第一: 生成表头 模版 (中文表头 以及 模版对应的模版字段)
private void buildTempLate(String templateFileName) {
try (ExcelWriter excelWriter = EasyExcel.write(templateFileName).needHead(false).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
// 模版表头
final List<List<String>> headNameList = getHeadNameList();
// 模版 占位符
final List<List<String>> tempKeyList = getTempKeyList();
excelWriter.write(headNameList, writeSheet);
excelWriter.write(tempKeyList, writeSheet);
excelWriter.finish();
}
}
// 生成动态表头中文名
private List<List<String>> getHeadNameList() {
// 模拟从数据库中加载配置信息
final List<List<String>> headList = new ArrayList<>();
// 加载学校表头
final List<String> schList = new ArrayList<>(List.of("学校名称","学校地址","学校电话"));
// 加载学生表头
final List<String> stdList = new ArrayList<>(List.of("学生名称","学生年龄","学生年级","学生班级"));
// 加载课程表头
final List<String> courseList = new ArrayList<>(List.of("课程名称","课程类型","课程编码"));
schList.addAll(stdList);
schList.addAll(courseList);
headList.add(schList);
return headList;
}
// 生成动态表头占位符
private List<List<String>> getTempKeyList() {
// 模拟从数据库中加载配置信息
final List<List<String>> headList = new ArrayList<>();
// 加载学校表头
final List<String> schList = new ArrayList<>(List.of("{.schName}", "{.schAddr}", "{.schTel}"));
// 加载学生表头
final List<String> stdList = new ArrayList<>(List.of("{.stdName}", "{.stdAge}", "{.stdGrade}", "{.stdStuClass}"));
// 加载课程表头
final List<String> courseList = new ArrayList<>(List.of("{.couName}", "{.couType}", "{.couCode}"));
schList.addAll(stdList);
schList.addAll(courseList);
headList.add(schList);
return headList;
}
最终生成的效果如下:
第二: 用easyExcel的fill 方法 ,对 模版数据进行填充
// 用easyExcel的fill 方法 ,对 模版数据进行填充
try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet().build();
// 模拟从DB中查询数据
final List<Map<String, Object>> dataList = dyNamicData();
excelWriter.fill(dataList, writeSheet);
excelWriter.finish();
}
最终生成的文件效果如下:
PS:对于表头来源于多张表且每张表的属性的情况下,其实也通过建立一个大而全的 BO 对象来实现,但如果某张表表中的属性是动态扩展的,比如课程表的属性不固定:如不同学校 的课程 表 可能会有 课程积分,是否必修等不同属性时,如果此时课程表的属性根据配置需要进行动态扩展,且属性名不可预知,建立一个大二全的BO 是不可行的)。
如果需要源码:https://github.com/shao139772/easyExcelQuickStart/tree/master
附录:使用easyExcel 导出遇到的一些问题
1 wps 正常打开,office 打开提示 发现 XX.xlsx中的部分内容有问题,是否让我们尽量尝试修复?如果您信任 次工作薄的源,请打击“是”。
此问题是由于,表格设置边框样式,且模版表头中 有 N个属性, 但是用于填充的list 集合中存储对象的属性 不足N个,就会给出此提示。
例如:模版中 有{.stdName}、{.stdAge}、{.stdSex} 3个 属性,但是从数据库中查询出来的对象属性 只有 {.stdName}、{.stdSex} 2个属性,就会有此问题。
2 莫名的合并了单元格
此问题是由于,用于填充的list 集合中存储对象的属性为null ,就会出现此效果。
例如:模版中 表头有{.stdName}、{.stdAge}、{.stdSex}、{.stdGrade} 4个 属性,但是从数据库中查询出来的对象属性 中 stdage 为 10,stdSex 为null。 就会有此问题。
总结: 模版中定义了的属性一定要出现在数据库中查询后的对象属性,不能没有,且不要为null