diff --git a/README.md b/README.md index d57f83a..4c5ec78 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ ---------------------------------- # JAVA解析Excel工具easyexcel -Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到KB级别,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便 +Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便 ## 相关文档 * [关于软件](/abouteasyexcel.md) * [快速使用](/quickstart.md) @@ -21,118 +21,50 @@ Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都 * [更新记事](/update.md) * [English-README](/easyexcel_en.md) ## 二方包 - +```xml com.alibaba easyexcel - {latestVersion} + 2.0.0 - -## 最新版本:1.1.2-beta4 +``` +## 最新版本:2.0.0 ## 维护者 姬朋飞(玉霄) ## 快速开始 ### 读Excel -测试代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/ReadTest.java](/src/test/java/com/alibaba/easyexcel/test/ReadTest.java) - -读07版小于1000行数据返回List> -``` -List data = EasyExcelFactory.read(inputStream, new Sheet(1, 0)); -``` -读07版小于1000行数据返回List -``` -List data = EasyExcelFactory.read(inputStream, new Sheet(2, 1,JavaModel.class)); -``` -读07版大于1000行数据返回List> -``` -ExcelListener excelListener = new ExcelListener(); -EasyExcelFactory.readBySax(inputStream, new Sheet(1, 1), excelListener); +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) + +```java + /** + * 最简单的读 + *
  • 1. 创建excel对应的实体对象 参照{@link DemoData} + *
  • 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} + *
  • 3. 直接读即可 + */ + @Test + public void simpleRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + EasyExcelFactory.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead().finish(); + } ``` -读07版大于1000行数据返回List -``` -ExcelListener excelListener = new ExcelListener(); -EasyExcelFactory.readBySax(inputStream, new Sheet(2, 1,JavaModel.class), excelListener); -``` -读03版方法同上 ### 写Excel -测试代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/WriteTest.java](/src/test/java/com/alibaba/easyexcel/test/WriteTest.java) -没有模板 -```OutputStream out = new FileOutputStream("/Users/jipengfei/2007.xlsx"); -ExcelWriter writer = EasyExcelFactory.getWriter(out); - -//写第一个sheet, sheet1 数据全是List 无模型映射关系 -Sheet sheet1 = new Sheet(1, 3); -sheet1.setSheetName("第一个sheet"); -//设置列宽 设置每列的宽度 -Map columnWidth = new HashMap(); -columnWidth.put(0,10000);columnWidth.put(1,40000);columnWidth.put(2,10000);columnWidth.put(3,10000); -sheet1.setColumnWidthMap(columnWidth); -sheet1.setHead(createTestListStringHead()); -//or 设置自适应宽度 -//sheet1.setAutoWidth(Boolean.TRUE); -writer.write1(createTestListObject(), sheet1); - -//写第二个sheet sheet2 模型上打有表头的注解,合并单元格 -Sheet sheet2 = new Sheet(2, 3, JavaModel1.class, "第二个sheet", null); -sheet2.setTableStyle(createTableStyle()); -writer.write(createTestListJavaMode(), sheet2); - -//写第三个sheet包含多个table情况 -Sheet sheet3 = new Sheet(3, 0); -sheet3.setSheetName("第三个sheet"); -Table table1 = new Table(1); -table1.setHead(createTestListStringHead()); -writer.write1(createTestListObject(), sheet3, table1); - -//写sheet2 模型上打有表头的注解 -Table table2 = new Table(2); -table2.setTableStyle(createTableStyle()); -table2.setClazz(JavaModel1.class); -writer.write(createTestListJavaMode(), sheet3, table2); - -//关闭资源 -writer.finish(); -out.close(); -``` -有模板 -```InputStream inputStream = new BufferedInputStream(new FileInputStream("/Users/jipengfei/temp.xlsx")); -OutputStream out = new FileOutputStream("/Users/jipengfei/2007.xlsx"); -ExcelWriter writer = EasyExcelFactory.getWriterWithTemp(inputStream,out,ExcelTypeEnum.XLSX,true); - -//写第一个sheet, sheet1 数据全是List 无模型映射关系 -Sheet sheet1 = new Sheet(1, 3); -sheet1.setSheetName("第一个sheet"); -//设置列宽 设置每列的宽度 -Map columnWidth = new HashMap(); -columnWidth.put(0,10000);columnWidth.put(1,40000);columnWidth.put(2,10000);columnWidth.put(3,10000); -sheet1.setColumnWidthMap(columnWidth); -sheet1.setHead(createTestListStringHead()); -//or 设置自适应宽度 -//sheet1.setAutoWidth(Boolean.TRUE); -writer.write1(createTestListObject(), sheet1); - -//写第二个sheet sheet2 模型上打有表头的注解,合并单元格 -Sheet sheet2 = new Sheet(2, 3, JavaModel1.class, "第二个sheet", null); -sheet2.setTableStyle(createTableStyle()); -writer.write(createTestListJavaMode(), sheet2); - -//写第三个sheet包含多个table情况 -Sheet sheet3 = new Sheet(3, 0); -sheet3.setSheetName("第三个sheet"); -Table table1 = new Table(1); -table1.setHead(createTestListStringHead()); -writer.write1(createTestListObject(), sheet3, table1); - -//写sheet2 模型上打有表头的注解 -Table table2 = new Table(2); -table2.setTableStyle(createTableStyle()); -table2.setClazz(JavaModel1.class); -writer.write(createTestListJavaMode(), sheet3, table2); - -//关闭资源 -writer.finish(); -out.close(); +DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java) +```java + /** + * 最简单的写 + *
  • 1. 创建excel对应的实体对象 参照{@link com.alibaba.easyexcel.test.demo.write.DemoData} + *
  • 2. 直接写即可 + */ + @Test + public void simpleWrite() { + String fileName = TestFileUtil.getPath() + "write.xlsx"; + // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后千万别忘记 finish + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcelFactory.write(fileName, DemoData.class).sheet("模板").doWrite(data()).finish(); + } ``` ### web下载实例写法 diff --git a/pom.xml b/pom.xml index 559fe3b..2913553 100644 --- a/pom.xml +++ b/pom.xml @@ -81,6 +81,7 @@ ehcache 3.7.1 + ch.qos.logback logback-classic @@ -99,6 +100,18 @@ 1.18.8 test + + org.springframework.boot + spring-boot + 1.5.21.RELEASE + test + + + org.springframework.boot + spring-boot-starter-web + 1.5.21.RELEASE + test + junit junit diff --git a/quickstart.md b/quickstart.md index afba795..296e276 100644 --- a/quickstart.md +++ b/quickstart.md @@ -12,17 +12,6 @@ ## *写Excel时一个sheet可以写多个Table
    ## *写Excel时候自定义是否需要写表头
    -## 二方包依赖 - -使用前最好咨询下最新版,或者到mvn仓库搜索先easyexcel的最新版 - -``` - - com.alibaba - easyexcel - 1.0.0-RELEASE - -``` ## 读Excel 使用easyexcel解析03、07版本的Excel只是ExcelTypeEnum不同,其他使用完全相同,使用者无需知道底层解析的差异。 diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoData.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoData.java new file mode 100644 index 0000000..db5afaa --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoData.java @@ -0,0 +1,17 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.Date; + +import lombok.Data; + +/** + * 基础数据类.这里的排序和excel里面的排序一致 + * + * @author zhuangjiaju + **/ +@Data +public class DemoData { + private String string; + private Date date; + private Double doubleData; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java new file mode 100644 index 0000000..6887ebc --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java @@ -0,0 +1,49 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson.JSON; + +/** + * 模板的读取类 + * + * @author zhuangjiaju + */ +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("存储数据库成功!"); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameData.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameData.java new file mode 100644 index 0000000..304be59 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameData.java @@ -0,0 +1,28 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.Data; + +/** + * 基础数据类 + * + * @author zhuangjiaju + **/ +@Data +public class IndexOrNameData { + /** + * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配 + */ + @ExcelProperty(index = 2) + private Double doubleData; + /** + * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据 + */ + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameDataListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameDataListener.java new file mode 100644 index 0000000..ad6a58f --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameDataListener.java @@ -0,0 +1,49 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson.JSON; + +/** + * 模板的读取类 + * + * @author zhuangjiaju + */ +public class IndexOrNameDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(IndexOrNameDataListener.class); + /** + * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + List list = new ArrayList(); + + @Override + public void invoke(IndexOrNameData 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("存储数据库成功!"); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java new file mode 100644 index 0000000..bbd8731 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java @@ -0,0 +1,51 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.io.File; + +import org.junit.Ignore; +import org.junit.Test; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcelFactory; +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.read.metadata.ReadSheet; + +/** + * 读的常见写法 + * + * @author zhuangjiaju + */ +@Ignore +public class ReadTest { + /** + * 最简单的读 + *
  • 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 然后千万别忘记 finish + EasyExcelFactory.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead().finish(); + + // 写法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(); + } + + /** + * 指定列的下标或者列名 + *
  • 使用{@link com.alibaba.excel.annotation.ExcelProperty}注解即可 + */ + @Test + public void indexOrNameRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里默认读取第一个sheet 然后千万别忘记 finish + EasyExcelFactory.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead().finish(); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/web/EasyexcelApplication.java b/src/test/java/com/alibaba/easyexcel/test/demo/web/EasyexcelApplication.java new file mode 100644 index 0000000..d12cf85 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/web/EasyexcelApplication.java @@ -0,0 +1,12 @@ +package com.alibaba.easyexcel.test.demo.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class EasyexcelApplication { + + public static void main(String[] args) { + SpringApplication.run(EasyexcelApplication.class, args); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java new file mode 100644 index 0000000..143a4e4 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java @@ -0,0 +1,29 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.Data; + +/** + * 基础数据类.行高 列宽可以忽略 + * + * @author zhuangjiaju + **/ +@Data +public class DemoData { + + @ExcelProperty("字符串标题") + private String string; + /** + * 指定写到excel的格式 + */ + @ExcelProperty("日期标题") + private Date date; + /** + * 数字转成百分比,默认数字存到excel是数字,不存在格式,现在要变成百分比就变成文本了,所以要指定转换器 + */ + @ExcelProperty("数字标题") + private Double doubleData; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java new file mode 100644 index 0000000..af34472 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -0,0 +1,44 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcelFactory; + +/** + * 写的常见写法 + * + * @author zhuangjiaju + */ +@Ignore +public class WriteTest { + /** + * 最简单的写 + *
  • 1. 创建excel对应的实体对象 参照{@link com.alibaba.easyexcel.test.demo.write.DemoData} + *
  • 2. 直接写即可 + */ + @Test + public void simpleWrite() { + String fileName = TestFileUtil.getPath() + "write" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后千万别忘记 finish + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcelFactory.write(fileName, DemoData.class).sheet("模板").doWrite(data()).finish(); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("字符串" + 0); + data.setDate(new Date()); + data.setDoubleData(0.56); + list.add(data); + } + return list; + } +} diff --git a/src/test/resources/demo/demo.xlsx b/src/test/resources/demo/demo.xlsx new file mode 100644 index 0000000..1fa2eda Binary files /dev/null and b/src/test/resources/demo/demo.xlsx differ diff --git a/update.md b/update.md index 1414957..6058627 100644 --- a/update.md +++ b/update.md @@ -1,39 +1,25 @@ -# 1.0.1 - -完善测试用例,防止歧义,模型字段映射不上时候有抛异常,改为提醒。 - -# 1.0.2 - -修复拿到一行数据后,存到list中,但最后处理时候变为空的bug。 - -# 1.0.3 - -修复无@ExcelProperty标注的多余字段时候报错。 - -# 1.0.4 - -修复日期类型转换时候数字问题。基础模型支持字段类型int,long,double,boolean,date,string - -# 1.0.5 - -优化类型转换的性能。 - -# 1.0.6 - -增加@ExcelColumnNum,修复字符串前后空白,增加过滤功能。 -# 1.0.8 - -如果整行excel数据全部为空,则不解析返回。完善多sheet的解析。 - -# 1.0.9 - -修复excel超过16列被覆盖的问题,修复数据只有一行时候无法透传的bug。 - - -# 1.2.1 - -修复POI在大并发情况下创建临时目录失败的bug - +# 2.0.0 +* 优化读写逻辑 +* 优化读写对外接口 +* 加入转换器,方便格式转换 +* 极大优化读大文件的内存和效率 # 1.2.4 - 修复read()方法存在的bug +# 1.2.1 +修复POI在大并发情况下创建临时目录失败的bug +# 1.0.9 +修复excel超过16列被覆盖的问题,修复数据只有一行时候无法透传的bug。 +# 1.0.8 +如果整行excel数据全部为空,则不解析返回。完善多sheet的解析。 +# 1.0.6 +增加@ExcelColumnNum,修复字符串前后空白,增加过滤功能。 +# 1.0.5 +优化类型转换的性能。 +# 1.0.4 +修复日期类型转换时候数字问题。基础模型支持字段类型int,long,double,boolean,date,string +# 1.0.3 +修复无@ExcelProperty标注的多余字段时候报错。 +# 1.0.2 +修复拿到一行数据后,存到list中,但最后处理时候变为空的bug。 +# 1.0.1 +完善测试用例,防止歧义,模型字段映射不上时候有抛异常,改为提醒。 \ No newline at end of file