EEC(Excel Export Core)是一个Excel读取和写入工具,目前支持xlsx格式的读取、写入以及xls格式的读取(xls支持版本BIFF8也就是excel 97~2003格式)。
EEC的设计初衷是为了解决Apache POI速度慢,高内存且API臃肿的诟病,EEC的底层并没有使用Apache POI包,所有的底层读写代码均自己实现,事实上EEC仅依懒dom4j
和logback
,前者用于小文件xml读取,后者统一日志接口。
EEC最大特点是高速
和低内存
,如果在项目中做数据导入导出功能,选用EEC将为你带来极大的便利,同时它的可扩展
能力也不弱。
使用inlineStr
模式的情况下EEC的读写内存可以控制在10MB以下,SharedString
模式也可以控制在16MB以下。这里 有关于EEC的压力测试,最低可以在6MB的情况下完成1,000,000行x29列数据的读写。
EEC采用单线程、高IO设计,所以多核心、高内存并不能显著提高速度,高主频和一块好SSD能显著提升速度。
EEC在JVM参数-Xmx6m -Xms1m
下读写1,000,000行x29列
内存使用截图
写文件
读文件
目前已实现worksheet类型有
- ListSheet // 对象数组
- ListMapSheet // Map数组
- StatementSheet // PreparedStatement
- ResultSetSheet // ResultSet支持(多用于存储过程)
- EmptySheet // 空worksheet
- CSVSheet // 支持csv与xlsx互转
也可以继承已知Worksheet来实现自定义数据源,比如微服务,mybatis或者其它RPC
EEC并不是一个功能全面的Excel操作工具类,它功能有限并不能用它来完全替代Apache POI,它最擅长的操作是表格处理。比如将数据库表导出为Excel或者读取Excel表格内容到Stream或数据库。
阅读WIKI 了解更多用法(编写中)
- 支持大数据量导出,行数无上限。如果数据量超过单个sheet上限会自动分页。(xlsx单sheet最大1,048,576行)
- 超低内存,无论是xlsx还是xls格式,大部分情况下可以在10MB以内完成十万级甚至百万级行数据读写。
- 支持 对象数组 和 Map数组 导出。
- 可以为某列设置阀值高亮显示。如导出学生成绩时低于60分的单元格背景标黄显示。
- 导出excel默认隔行变色(俗称斑马线),利于阅读
- 设置列宽自动调节(功能未完善)
- 设置水印(文字,本地&网络图片)
- 提供Watch窗口查看操作细节也可以做进度条。
- ExcelReader采用stream方式读取文件,只有当你操作某行数据的时候才会执行读文件,而不会将整个文件读入到内存。
- Reader支持iterator或者stream+lambda操作sheet或行数据,你可以像操作集合类一样读取并操作excel
pom.xml添加
<dependency>
<groupId>org.ttzero</groupId>
<artifactId>eec</artifactId>
<version>${eec.version}</version>
</dependency>
所有测试生成的excel文件均放在target/excel目录下,可以使用mvn clean
清空。测试命令可以使用mvn clean test
清空先前文件避免找不到测试结果文件
对象数组导出时可以在对象上使用注解@ExcelColumn("column name")
来设置excel头部信息,未添加ExcelColumn注解标记的属性将不会被导出,也可以通过调用forceExport
方法来强制导出。
private int id; // not export
@ExcelColumn("渠道ID")
private int channelId;
@ExcelColumn
private String account;
@ExcelColumn("注册时间")
private Timestamp registered;
默认情况下导出的列顺序与字段在对象中的定义顺序一致,也可以设置colIndex
或者在addSheet
时重置列头顺序。
public void testWrite(List<Student> students) throws IOException {
// 创建一个名为"test object"的excel文件,指定作者,不指定时默认取系统登陆名
new Workbook("test object", "guanquan.wang")
// 添加一个worksheet,可以通过addSheet添加多个worksheet
.addSheet(new ListSheet<>("学生信息", students))
// 指定输出位置,如果做文件导出可以直接输出到`respone.getOutputStream()`
.writeTo(Paths.get("f:/excel"));
}
高亮和数据转换是通过@FunctionalInterface
实现,下面展示如何将低下60分的成绩输出为"不合格"并将单元格标红
public void testStyleConversion(List<Student> students) throws IOException {
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) -> {
if ((int)o < 60) {
style = Styles.clearFill(style)
| sst.addFill(new Fill(PatternType.solid, Color.orange));
}
return style;
})
)
)
.writeTo(Paths.get("f:/excel"));
}
内容如下图
EEC使用ExcelReader#read
静态方法读文件,其内部采用流式操作,当使用某一行数据时才会真正读入内存,所以即使是GB级别的Excel文件也只占用少量内存。
默认的ExcelReader仅读取单元格的值而忽略单元格的公式,可以使用ExcelReader#parseFormula
方法使Reader解析单元格的公式。
下面展示一些常规的读取方法
public void streamRead() {
try (ExcelReader reader = ExcelReader.read(defaultPath.resolve("用户注册.xlsx"))) {
reader.sheets().flatMap(Sheet::rows).forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* read excel to object array
*/
public void readToList() {
try (ExcelReader reader = ExcelReader.read(defaultPath.resolve("用户注册.xlsx"))) {
// 读取所有worksheet
Regist[] array = reader.sheets()
// 读取数据行
.flatMap(Sheet::dataRows)
// 将每行数据转换为Regist对象
.map(row -> row.to(Regist.class))
// 转数组或者List
.toArray(Regist[]::new);
// TODO 其它逻辑
} catch (IOException e) {
e.printStackTrace();
}
}
reader.sheets()
.flatMap(Sheet::dataRows)
.map(row -> row.to(Regist.class))
.filter(e -> "iOS".equals(e.platform()))
.collect(Collectors.toList());
以上代码相当于select * from 用户注册 where platform = 'iOS'
读取xls格式的方法与读取xlsx格式完全一样,读取文件时不需要判断是xls格式还是xlsx格式,因为EEC为其提供了完全一样的接口,内部会根据文件头去判断具体类型, 这种方式比判断文件后缀准确得多。
pom.xml添加
<dependency>
<groupId>org.ttzero</groupId>
<artifactId>eec-e3-support</artifactId>
<version>0.5.0</version>
</dependency>
你可以在 search.maven.org 查询eec-e3-support版本,两个工具的兼容性 参考此表
- CSV => Excel 向Workbook中添加一个
CSVSheet
即可 - Excel => CSV 读Excel后通过Worksheet调用
saveAsCSV
代码示例
// CSV转Excel
new Workbook("csv path test", author)
.addSheet(new CSVSheet(csvPath)) // 添加CSVSheet并指定csv路径
.writeTo(getOutputTestPath());
// Excel转CSV
try (ExcelReader reader = ExcelReader.read(testResourceRoot().resolve("1.xlsx"))) {
// 读取Excel并保存为CSV格式
reader.sheet(0).saveAsCSV(getOutputTestPath());
} catch (IOException e) {
e.printStackTrace();
}
- 增加StyleDesign用于样式处理(单元格或者整行样式处理)
- 增加FreezePanes用于冻结网格
- 修改部分BUG(#227,#232,#238,#243)
- 读取文件支持自定义注解转对象(#237)
- 提高对Numbers转xlsx的兼容性
- 值转换从原来的int类型扩大为Object
- 增加@RowNum注解,用于注入行号
- 修改ListSheet.EntryColumn的访问权限,方便实现更多高级特性
- 支持单列数字无表头导出,现在可以简单的导出
List<String>
数据 - 修复已知BUG(#197,#202,#205,#219)
- 将com.google.common包重命名为org.ttzero.excel.common解决内嵌引起的包冲突(#200)
- 支持xls获取图片
@ExcelColumn
注解增加colIndex
属性,用于指定列顺序(#188)- 读取文件时
Worksheet#getIndex()
方法返回Sheet在文件中的下标而非id,并取消按id排序(#193) - 修复部分BUG(#182,#190)
- Hotfix:HeaderStyle注解设置某列cell颜色会影响所有表头样式