EEC(Excel Export Core)是一款轻量且高效的Excel读写工具,它具有包体小、接入代码量少和运行时消耗资源少等优点
EEC的设计初衷是为了解决Apache POI内存高、速度慢且API臃肿的诟病。EEC的底层并不依赖POI包,所有的底层代码均自己实现,事实上EEC仅依赖dom4j
和slf4j
,前者用于小文件xml读取,后者统一日志接口。
EEC在JVM参数-Xmx10m -Xms10m
下读写100w行x29列内存使用截图,下载 eec-benchmark 项目进行性能测试
EEC支持大多数日常应用场景,最擅长的是表格处理,比如转对象数组、转Map数组、内容检查等导入/导出常见功能。
目前已实现worksheet类型有六种,也可以继承已有Worksheet来实现自定义数据源
- ListSheet // 对象数组
- ListMapSheet // Map数组
- StatementSheet // PreparedStatement
- ResultSetSheet // ResultSet支持(多用于存储过程)
- EmptySheet // 空worksheet
- CSVSheet // 支持csv与xlsx互转
- 支持大数据量导出,行数无上限,超过单个Sheet上限会自动分页
- 超低内存,无论是xlsx还是xls格式,大部分情况下可以在10MB以内完成十万级甚至百万级行数据读写
- 支持动态样式,如导出库存时将低于预警阈值的行背景标黄显示
- 支持一键设置斑马线,利于阅读
- 自适应列宽对中文更精准
- 采用Stream流读文件,按需加载不会将整个文件读入到内存
- 支持Iterator和Stream+Lambda读文件,你可以像操作集合类一样操作Excel
- 支持csv与excel格式相互转换
阅读WIKI 了解更多用法
国内用户可访问Gitee, 在Gitee提issue开发者也同样会及时回复
pom.xml添加
<dependency>
<groupId>org.ttzero</groupId>
<artifactId>eec</artifactId>
<version>${eec.version}</version>
</dependency>
对象数组导出时可以在对象上使用注解@ExcelColumn("列名")
来设置excel头部信息
@ExcelColumn("渠道ID")
private int channelId;
@ExcelColumn
private String account;
默认情况下导出的列顺序与字段在对象中的定义顺序一致,可以通过指定colIndex
或指定Column数组来重置顺序。
// 创建一个名为"test object"的excel文件
new Workbook("test object")
// 添加"工作表"并指定导出数据,可以通过addSheet添加多个worksheet
.addSheet(new ListSheet<>("学生信息", students))
// 指定输出位置,如果做文件导出可以直接输出到`respone.getOutputStream()`
.writeTo(Paths.get("f:/excel"));
动态样式和数据转换都是使用@FunctionalInterface
实现,通常用于突出或高亮显示一些重要的单元格或行,下面展示如何将低下60分的成绩输出为"不合格"并将整行标为橙色
new Workbook("2021小五班期未考试成绩")
.addSheet(new ListSheet<>("期末成绩", students
, new Column("学号", "id", int.class)
, new Column("姓名", "name", String.class)
, new Column("成绩", "score", int.class, n -> (int) n < 60 ? "不合格" : n)
).setStyleProcessor((o, style, sst) ->
o.getScore() < 60 ? sst.modifyFill(style, new Fill(PatternType.solid, Color.orange)) : style)
).writeTo(Paths.get("f:/excel"));
效果如下图
// 测试类
public static class WidthTestItem {
@ExcelColumn(value = "整型", format = "#,##0_);[Red]-#,##0_);0_)")
private Integer nv;
@ExcelColumn("字符串(en)")
private String sen;
@ExcelColumn("字符串(中文)")
private String scn;
@ExcelColumn(value = "日期时间", format = "yyyy-mm-dd hh:mm:ss")
private Timestamp iv;
}
new Workbook("Auto Width Test")
.setAutoSize(true) // <- 自适应列宽
.addSheet(new ListSheet<>(randomTestData()))
.writeTo(Paths.get("f:/excel"));
EEC使用多个ExcelColumn注解来实现多级表头,名称一样的行或列将自动合并
public static class RepeatableEntry {
@ExcelColumn("运单号")
private String orderNo;
@ExcelColumn("收件地址")
@ExcelColumn("省")
private String rProvince;
@ExcelColumn("收件地址")
@ExcelColumn("市")
private String rCity;
@ExcelColumn("收件地址")
@ExcelColumn("详细地址")
private String rDetail;
@ExcelColumn("收件人")
private String recipient;
@ExcelColumn("寄件地址")
@ExcelColumn("省")
private String sProvince;
@ExcelColumn("寄件地址")
@ExcelColumn("市")
private String sCity;
@ExcelColumn("寄件地址")
@ExcelColumn("详细地址")
private String sDetail;
@ExcelColumn("寄件人")
private String sender;
}
现在使用普通的ListSheet就可以导出漂亮的报表。示例请跳转到 WIKI
记帐类
统计类
导出图片时可以设置图片样式使其更美观,关于图片样式请参考1-导出Excel#导出图片
EEC使用ExcelReader#read
静态方法读文件,因其支持标准Stream所以可以直接使用map
、filter
和collect
等JDK内置函数,读取Excel就像操作集合类一样简单,极大降低学习成本。
try (ExcelReader reader = ExcelReader.read(Paths.get("./用户注册.xlsx"))) {
// 读取所有worksheet并输出
reader.sheets().flatMap(Sheet::rows).forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
try (ExcelReader reader = ExcelReader.read(Paths.get("./User.xlsx"))) {
// 读取第1个Sheet页
List<User> users = reader.sheet(0)
// 指定第6行为表头,前5行为概要信息
.header(6)
// 读取数据行
.rows()
// 将每行数据转换为User象
.map(row -> row.to(User.class))
// 收集为List或数组进行后续处理
.collect(Collectors.toList());
} catch (IOException e) {
e.printStackTrace();
}
EEC支持Stream的大部分功能,以下代码展示过滤平台为"iOS"的注册用户
reader.sheet(0).header(6)
.rows()
.filter(row -> "iOS".equals(row.getString("platform"))) // 过滤平台为"iOS"的用户
.map(row -> row.to(Regist.class))
.collect(Collectors.toList());
多级表头可以使用header
方法来指定表头所在的多个行号
reader.sheet(0)
.header(1, 2) // <- 指定第1、2行均为表头
.map(Row::toMap) // <- Row 转 Map
.forEach(System.out::println)
多级表头将以A1:A2:A3
这种格式进行纵向拼接,像上面第4个示例中的运单数据读取结果将以运单号
,收件地址:省
,收件地址:市
呈现,这样就可以解决出现两个省
、市
导致错乱的问题
更多关于多表头使用方法可以参考 WIKI
pom.xml添加如下依赖,添加好后即完成了xls的兼容,是的!你不需要为xls格式添加任何一行代码。
<dependency>
<groupId>org.ttzero</groupId>
<artifactId>eec-e3-support</artifactId>
<version>${eec-e3-support.version}</version>
</dependency>
读取xls的方法与xlsx完全一样,外部不需要判断是哪种格式,EEC为其提供了完全一样的接口,内部会根据文件头去判断具体类型,这种方式比简单判断文件后缀准确得多。
两个工具的兼容性 参考此表
- CSV => Excel:向Workbook中添加一个
CSVSheet
工作表 - Excel => CSV:读Excel时调用
saveAsCSV
另存为csv格式
代码示例
// 直接保存为csv生成测试文件,对于数据量较多的场合也可以使用#more方法分批获取数据
new Workbook()
.addSheet(createTestData())
.saveAsCSV() // 指定输出格式为csv
.writeTo(Paths.get("d:\\abc.csv"));
// CSV转Excel
new Workbook()
.addSheet(new CSVSheet(Paths.get("d:\\abc.csv"))) // 添加CSVSheet并指定csv路径
.writeTo(Paths.get("d:\\abc.xlsx"));
// Excel转CSV
try (ExcelReader reader = ExcelReader.read(Paths.get("d:\\abc.xlsx"))) {
// 读取Excel使用saveAsCSV保存为CSV格式
reader.sheet(0).saveAsCSV(Paths.get("./"));
} catch (IOException e) {
e.printStackTrace();
}
- logback安全更新
- 新增全属性工作表FullSheet以读取更多属性,它集合了MergeSheet和CalcSheet的功能
- 新增扩展属性AutoFilter用于添加列筛选功能
- 修复继承自ListSheet的工作表初始无法获取对象类型导致单元格空白的问题
- 修复部分场景下边框颜色无法设置的问题
- 修复部分Excel的indexed颜色与标准有所不同导致获取颜色不正确的问题
- 修复部分场景读取Excel发生IndexOutOfBound异常
- 修复HeaderStyle注解设置样式时,字段样式被全局样式替换的问题
- 移除watch改用slf4j输出日志
- 新增进度窗口
onProgress
- 优化自适应列宽算法使其支持更多字体和大小
- 数据转换器功能增强,ExcelColumn增加converter属性以支持导出/导入时双向数据转换(#362)
- 支持读取xlsx格式wps的内嵌图片(#363)
- 部分类的注释改为中文,后续会将全部注释改为中文
- 优化ExcelReader性能,性能提升100%~300%
- 增加
setHeaderColumnReadOption
方法提高ExcelReader丰富性 - 修复读取16进制转义字符时出现乱码问题
- 修复非法UTF8字符导致写文件异常
- 无数据且能获取表头信息时正常写表头(#361)
- 屏蔽JDK17以上版本使用ExcelReader抛异常的问题
- 修复单元格长度过长导致内容错位的异常(#354)
- 支持导出图片