# easyexcel核心功能 ## 目录 ### 读 DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/demo/read/ReadTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java) * [最简单的读](#simpleRead) ### 写 ## *读任意大小的03、07版Excel不会OO] ## *读Excel自动通过注解,把结果映射为java模型 ## *读Excel支持多sheet ## *读Excel时候是否对Excel内容做trim()增加容错 ## *写小量数据的03版Excel(不要超过2000行) ## *写任意大07版Excel不会OOM ## *写Excel通过注解将表头自动写入Excel ## *写Excel可以自定义Excel样式 如:字体,加粗,表头颜色,数据内容颜色 ## *写Excel到多个不同sheet ## *写Excel时一个sheet可以写多个Table ## *写Excel时候自定义是否需要写表头 ## 读excel样例 ### 最简单的读 #### excel示例 ![img](img/readme/quickstart/read/demo.png) #### 对象 ```java @Data public class DemoData { private String string; private Date date; private Double doubleData; } ``` #### 监听器 ```java public class DemoDataListener extends AnalysisEventListener { private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); /** * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收 */ private static final int BATCH_COUNT = 5; List list = new ArrayList(); @Override public void invoke(DemoData data, AnalysisContext context) { LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); list.add(data); if (list.size() >= BATCH_COUNT) { saveData(); list.clear(); } } @Override public void doAfterAllAnalysed(AnalysisContext context) { saveData(); LOGGER.info("所有数据解析完成!"); } /** * 加上存储数据库 */ private void saveData() { LOGGER.info("{}条数据,开始存储数据库!", list.size()); LOGGER.info("存储数据库成功!"); } } ``` #### 代码 ```java /** * 最简单的读 * 1. 创建excel对应的实体对象 参照{@link DemoData} * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} * 3. 直接读即可 */ @Test public void simpleRead() { // 写法1: String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 EasyExcelFactory.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); // 写法2: fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; ExcelReader excelReader = EasyExcelFactory.read(fileName, DemoData.class, new DemoDataListener()).build(); ReadSheet readSheet = EasyExcelFactory.readSheet(0).build(); excelReader.read(readSheet); // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 excelReader.finish(); } ``` ### 指定列的下标或者列名 #### excel示例 参照:[excel示例](#simpleReadExcel) #### 对象 ```java @Data public class IndexOrNameData { /** * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配 */ @ExcelProperty(index = 2) private Double doubleData; /** * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据 */ @ExcelProperty("字符串标题") private String string; @ExcelProperty("日期标题") private Date date; } ``` #### 监听器 参照:[监听器](#simpleReadListener) 只是泛型变了而已 #### 代码 ```java /** * 指定列的下标或者列名 * * 1. 创建excel对应的实体对象,并使用{@link ExcelProperty}注解. 参照{@link IndexOrNameData} * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link IndexOrNameDataListener} * 3. 直接读即可 */ @Test public void indexOrNameRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里默认读取第一个sheet EasyExcelFactory.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead(); } ``` ### 读多个sheet #### excel示例 参照:[excel示例](#simpleReadExcel) #### 对象 参照:[对象](#simpleReadObject) #### 监听器 参照:[监听器](#simpleReadListener) #### 代码 ```java /** * 读多个sheet,这里注意一个sheet不能读取多次,一定要多次需要重新读取文件 * 1. 创建excel对应的实体对象 参照{@link DemoData} * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} * 3. 直接读即可 */ @Test public void repeatedRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; ExcelReader excelReader = EasyExcelFactory.read(fileName, DemoData.class, new DemoDataListener()).build(); ReadSheet readSheet1 = EasyExcelFactory.readSheet(0).build(); ReadSheet readSheet2 = EasyExcelFactory.readSheet(1).build(); excelReader.read(readSheet1); excelReader.read(readSheet2); // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 excelReader.finish(); } ``` ### 日期、数字或者自定义格式转换 #### excel示例 参照:[excel示例](#simpleReadExcel) #### 对象 ```java @Data public class ConverterData { /** * 我自定义 转换器,不管数据库传过来什么 。我给他加上“自定义:” */ @ExcelProperty(converter = CustomStringStringConverter.class) private String string; /** * 这里用string 去接日期才能格式化。我想接收年月日格式 */ @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") private String date; /** * 我想接收百分比的数字 */ @NumberFormat("#.##%") private String doubleData; } ``` #### 监听器 参照:[监听器](#simpleReadListener) 只是泛型变了 #### 自定义转换器 ````java public class CustomStringStringConverter implements Converter { @Override public Class supportJavaTypeKey() { return String.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; } /** * 这里读的时候会调用 * * @param cellData * NotNull * @param contentProperty * Nullable * @param globalConfiguration * NotNull * @return */ @Override public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { return "自定义:" + cellData.getStringValue(); } /** * 这里是写的时候会调用 不用管 * * @param value * NotNull * @param contentProperty * Nullable * @param globalConfiguration * NotNull * @return */ @Override public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { return new CellData(value); } } ```` #### 代码 ```java /** * 日期、数字或者自定义格式转换 * * 默认读的转换器{@link DefaultConverterLoader#loadDefaultReadConverter()} * 1. 创建excel对应的实体对象 参照{@link ConverterData}.里面可以使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解 * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link ConverterDataListener} * 3. 直接读即可 */ @Test public void converterRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish EasyExcelFactory.read(fileName, ConverterData.class, new ConverterDataListener()) // 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。 // 如果就想单个字段使用请使用@ExcelProperty 指定converter // .registerConverter(new CustomStringStringConverter()) // 读取sheet .sheet().doRead(); } ``` ### 多行头 #### excel示例 参照:[excel示例](#simpleReadExcel) #### 对象 参照:[对象](#simpleReadObject) #### 监听器 参照:[监听器](#simpleReadListener) #### 代码 ```java /** * 多行头 * * 1. 创建excel对应的实体对象 参照{@link DemoData} * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} * 3. 设置headRowNumber参数,然后读。 这里要注意headRowNumber如果不指定, 会根据你传入的class的{@link ExcelProperty#value()}里面的表头的数量来决定行数, * 如果不传入class则默认为1.当然你指定了headRowNumber不管是否传入class都是以你传入的为准。 */ @Test public void complexHeaderRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish EasyExcelFactory.read(fileName, DemoData.class, new DemoDataListener()).sheet() // 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,因为默认会根据DemoData 来解析,他没有指定头,也就是默认1行 .headRowNumber(1).doRead(); } ``` ### 同步的返回 #### excel示例 参照:[excel示例](#simpleReadExcel) #### 对象 参照:[对象](#simpleReadObject) #### 代码 ```java /** * 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面 */ @Test public void synchronousRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish List list = EasyExcelFactory.read(fileName).head(DemoData.class).sheet().doReadSync(); for (Object obj : list) { DemoData data = (DemoData)obj; LOGGER.info("读取到数据:{}", JSON.toJSONString(data)); } // 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish list = EasyExcelFactory.read(fileName).sheet().doReadSync(); for (Object obj : list) { // 返回每条数据的键值对 表示所在的列 和所在列的值 Map data = (Map)obj; LOGGER.info("读取到数据:{}", JSON.toJSONString(data)); } } ``` ## 测试数据分析 ![POI usermodel PK easyexcel(Excel 2003).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/02c4bfbbab99a649788523d04f84a42f.png) ![POI usermodel PK easyexcel(Excel 2007).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/f6a8a19ec959f0eb564e652de523fc9e.png) ![POI usermodel PK easyexcel(Excel 2003) (1).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/26888f7ea1cb8dc56db494926544edf7.png) ![POI usermodel PK easyexcel(Excel 2007) (1).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/4de1ac95bdfaa4b1870b224af4f4cb75.png) 从上面的性能测试可以看出easyexcel在解析耗时上比poiuserModel模式弱了一些。主要原因是我内部采用了反射做模型字段映射,中间我也加了cache,但感觉这点差距可以接受的。但在内存消耗上差别就比较明显了,easyexcel在后面文件再增大,内存消耗几乎不会增加了。但poi userModel就不一样了,简直就要爆掉了。想想一个excel解析200M,同时有20个人再用估计一台机器就挂了。
* 默认读的转换器{@link DefaultConverterLoader#loadDefaultReadConverter()} *