diff --git a/README.md b/README.md index bf9871a8..535d1e55 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,9 @@ easyexcel [![Maven central](https://maven-badges.herokuapp.com/maven-central/com.alibaba/easyexcel/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/easyexcel) [![License](http://img.shields.io/:license-apache-brightgreen.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) -[QQ群:662022184](//shang.qq.com/wpa/qunwpa?idkey=53d9d821b0833e3c14670f007488a61e300f00ff4f1b81fd950590d90dd80f80) -[钉钉群:21960511](https://qr.dingtalk.com/action/joingroup?code=v1,k1,cchz6k12ci9B08NNqhNRFGXocNVHrZtW0kaOtTKg/Rk=&_dt_no_comment=1&origin=11) +[QQ群: 662022184](//shang.qq.com/wpa/qunwpa?idkey=53d9d821b0833e3c14670f007488a61e300f00ff4f1b81fd950590d90dd80f80) +[钉钉群: 21960511](https://qr.dingtalk.com/action/joingroup?code=v1,k1,cchz6k12ci9B08NNqhNRFGXocNVHrZtW0kaOtTKg/Rk=&_dt_no_comment=1&origin=11) +[官方网站: https://alibaba-easyexcel.github.io/](https://alibaba-easyexcel.github.io/) # JAVA解析Excel工具easyexcel 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模式。在上层做了模型转换的封装,让使用者更加简单方便 @@ -15,11 +16,9 @@ Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都 ![img](img/readme/large.png) ## 相关文档 -* [快速使用](/quickstart.md) +* [快速使用](https://alibaba-easyexcel.github.io/) * [关于软件](/abouteasyexcel.md) -* [常见问题](/problem.md) * [更新记事](/update.md) -* [English-README](/easyexcel_en.md) ## 维护者 姬朋飞(玉霄) diff --git a/docs/API.md b/docs/API.md index 032331a5..13635841 100644 --- a/docs/API.md +++ b/docs/API.md @@ -12,6 +12,7 @@ * `ExcelIgnore` 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段 * `DateTimeFormat` 日期转换,用`String`去接收excel日期格式的数据会调用这个注解。里面的`value`参照`java.text.SimpleDateFormat` * `NumberFormat` 数字转换,用`String`去接收excel数字格式的数据会调用这个注解。里面的`value`参照`java.text.DecimalFormat` +* `ExcelIgnoreUnannotated` 默认不加`ExcelProperty` 的注解的都会参与读写,加了不会参与 ### 参数 #### 通用参数 `ReadWorkbook`,`ReadSheet` 都会有的参数,如果为空,默认使用上级。 @@ -21,6 +22,7 @@ * `head` 与`clazz`二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用class。 * `clazz` 与`head`二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。 * `autoTrim` 字符串、表头等数据自动trim +* `password` 读的时候是否需要使用密码 #### ReadWorkbook(理解成excel对象)参数 * `excelType` 当前excel的类型 默认会自动判断 * `inputStream` 与`file`二选一。读取文件的流,如果接收到的是流就只用,不用流建议使用`file`参数。因为使用了`inputStream` easyexcel会帮忙创建临时文件,最终还是`file` @@ -36,6 +38,7 @@ * `ExcelIgnore` 默认所有字段都会写入excel,这个注解会忽略这个字段 * `DateTimeFormat` 日期转换,将`Date`写到excel会调用这个注解。里面的`value`参照`java.text.SimpleDateFormat` * `NumberFormat` 数字转换,用`Number`写excel会调用这个注解。里面的`value`参照`java.text.DecimalFormat` +* `ExcelIgnoreUnannotated` 默认不加`ExcelProperty` 的注解的都会参与读写,加了不会参与 ### 参数 #### 通用参数 `WriteWorkbook`,`WriteSheet` ,`WriteTable`都会有的参数,如果为空,默认使用上级。 @@ -53,6 +56,8 @@ * `templateInputStream` 模板的文件流 * `templateFile` 模板文件 * `autoCloseStream` 自动关闭流。 +* `password` 写的时候是否需要使用密码 +* `useDefaultStyle` 写的时候是否是使用默认头 #### WriteSheet(就是excel的一个Sheet)参数 * `sheetNo` 需要写入的编码。默认0 * `sheetName` 需要些的Sheet名称,默认同`sheetNo` diff --git a/problem.md b/problem.md deleted file mode 100644 index 9244148e..00000000 --- a/problem.md +++ /dev/null @@ -1,116 +0,0 @@ -# 常见问题汇总 - -## 1、系统环境变量缺失或JDK版本不支持 - -``` -java.lang.NullPointerException - at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264) - at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219) - at sun.awt.FontConfiguration.init(FontConfiguration.java:107) - at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774) - at sun.font.SunFontManager$2.run(SunFontManager.java:431) - at java.security.AccessController.doPrivileged(Native Method) - at sun.font.SunFontManager.(SunFontManager.java:376) - at sun.awt.FcFontManager.(FcFontManager.java:35) - at sun.awt.X11FontManager.(X11FontManager.java:57) - at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) - at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) - at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) - at java.lang.reflect.Constructor.newInstance(Constructor.java:423) - at java.lang.Class.newInstance(Class.java:442) - at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:83) - at java.security.AccessController.doPrivileged(Native Method) - at sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74) - at java.awt.Font.getFont2D(Font.java:495) - at java.awt.Font.canDisplayUpTo(Font.java:2080) - at java.awt.font.TextLayout.singleFont(TextLayout.java:470) - at java.awt.font.TextLayout.(TextLayout.java:531) - at org.apache.poi.ss.util.SheetUtil.getDefaultCharWidth(SheetUtil.java:275) - at org.apache.poi.xssf.streaming.AutoSizeColumnTracker.(AutoSizeColumnTracker.java:117) - at org.apache.poi.xssf.streaming.SXSSFSheet.(SXSSFSheet.java:79) - at org.apache.poi.xssf.streaming.SXSSFWorkbook.createAndRegisterSXSSFSheet(SXSSFWorkbook.java:656) - at org.apache.poi.xssf.streaming.SXSSFWorkbook.createSheet(SXSSFWorkbook.java:677) - at org.apache.poi.xssf.streaming.SXSSFWorkbook.createSheet(SXSSFWorkbook.java:83) - at com.alibaba.excel.write.context.GenerateContextImpl.buildCurrentSheet(GenerateContextImpl.java:93) - at com.alibaba.excel.write.ExcelBuilderImpl.addContent(ExcelBuilderImpl.java:53) - at com.alibaba.excel.ExcelWriter.write(ExcelWriter.java:44) -``` - -### 解决方法 - -该异常由于自己的环境变量缺少swing需要的字体配置,检查自己应用是否有配置-Djava.awt.headless=true,如果没有请加上该系统参数,可以解决问题。如果仍旧不行,在dockerfile中增加字体安装命令: -![粘贴图片.png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/a857edfbc8199db7bb35b9e99f1f57d5.png) -参考: -https://lark.alipay.com/aone355606/gfqllg/ulptif -https://stackoverflow.com/questions/30626136/cannot-load-font-in-jre-8 http://www.jianshu.com/p/c05b5fc71bd0 -## 2、xls格式错用xlsx方式解析 - -``` -org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException: The supplied data appears to be in the OLE2 Format. You are calling the part of POI that deals with OOXML (Office Open XML) Documents. You need to call a different part of POI to process this data (eg HSSF instead of XSSF) - at org.apache.poi.openxml4j.opc.internal.ZipHelper.verifyZipHeader(ZipHelper.java:172) - at org.apache.poi.openxml4j.opc.internal.ZipHelper.openZipStream(ZipHelper.java:229) - at org.apache.poi.openxml4j.opc.ZipPackage.(ZipPackage.java:97) - at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:342) - at com.alibaba.excel.read.v07.XlsxSaxAnalyser.(XlsxSaxAnalyser.java:46) - at com.alibaba.excel.read.ExcelAnalyserImpl.getSaxAnalyser(ExcelAnalyserImpl.java:30) - at com.alibaba.excel.read.ExcelAnalyserImpl.analysis(ExcelAnalyserImpl.java:51) - at com.alibaba.excel.ExcelReader.read(ExcelReader.java:55) - at read.v07.Read2007Xlsx.noModel(Read2007Xlsx.java:42) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) - at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) - at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) - at mockit.integration.junit4.internal.BlockJUnit4ClassRunnerDecorator.executeTest(BlockJUnit4ClassRunnerDecorator.java:126) - at mockit.integration.junit4.internal.BlockJUnit4ClassRunnerDecorator.invokeExplosively(BlockJUnit4ClassRunnerDecorator.java:104) - at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java) - at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) - at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) - at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) - at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) - at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) - at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) - at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) - at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) - at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) - at org.junit.runners.ParentRunner.run(ParentRunner.java:363) - at org.junit.runner.JUnitCore.run(JUnitCore.java:137) - at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) - at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51) - at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237) - at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) -``` - -### 解决方法 - -该异常时由于03版的xls,文件用07版的方式做解析的报错,请检查excelType是否设置错误。或者是不是手动去修改了excel文件名后缀的xls为xlsx。 - -## 3、xlsx错用xls解析 - -``` -org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF) - at org.apache.poi.poifs.storage.HeaderBlock.(HeaderBlock.java:152) - at org.apache.poi.poifs.storage.HeaderBlock.(HeaderBlock.java:140) - at org.apache.poi.poifs.filesystem.NPOIFSFileSystem.(NPOIFSFileSystem.java:302) - at org.apache.poi.poifs.filesystem.POIFSFileSystem.(POIFSFileSystem.java:87) - at com.alibaba.excel.read.v03.XlsSaxAnalyser.(XlsSaxAnalyser.java:55) - at com.alibaba.excel.read.ExcelAnalyserImpl.getSaxAnalyser(ExcelAnalyserImpl.java:27) - at com.alibaba.excel.read.ExcelAnalyserImpl.analysis(ExcelAnalyserImpl.java:51) - at com.alibaba.excel.ExcelReader.read(ExcelReader.java:55) - at read.v03.XLS2003FunctionTest.testExcel2003NoModel(XLS2003FunctionTest.java:31) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at mockit.integration.junit3.internal.JUnitTestCaseDecorator.runTest(JUnitTestCaseDecorator.java:142) - at mockit.integration.junit3.internal.JUnitTestCaseDecorator.originalRunBare(JUnitTestCaseDecorator.java:102) - at mockit.integration.junit3.internal.JUnitTestCaseDecorator.runBare(JUnitTestCaseDecorator.java:87) -``` -原理和原因大致同上 \ No newline at end of file diff --git a/quickstart.md b/quickstart.md index c9fdd7dd..dcf12add 100644 --- a/quickstart.md +++ b/quickstart.md @@ -1,1363 +1,2 @@ -# easyexcel核心功能 -## 各位读取文件务必使用2.0.3起 -2.0.0-beta1到2.0.2有小概率会丢失数字。 -## 目录 -### 前言 -#### 以下功能目前不支持 -* 单个文件的并发写入、读取 -* 读取图片 -* 宏 -* csv读取(这个后续可能会考虑) -#### 常见问题 -* 关于@Data,读写的对象都用到了[Lombok](https://www.projectlombok.org/),他会自动生成`get`,`set` ,如果不需要的话,自己创建对象并生成`get`,`set` 。 -* 出现`NoSuchMethodException`,`ClassNotFoundException`,`NoClassDefFoundError`极大概率是jar冲突,建议`clean`项目,或者统一`poi` 的版本,理论上来说`easyexcel`兼容poi的`3.17`,`4.0.1`,`4.1.0`所有较新版本 -* 如果在读的时候`Listener`里面需要使用spring的`@Autowired`,给`Listener`创建成员变量,然后在构造方法里面传进去。而别必须不让spring管理`Listener`,每次读取都要`new`一个。 -* 如果用`String`去接收数字,出现小数点等情况,这个是BUG,但是很难修复,后续版本会修复这个问题。目前请使用`@NumberFormat`注解,里面的参数就是调用了java自带的`NumberFormat.format`方法,不知道怎么入参的可以自己网上查询。 -* 10M以上xlsx读取很慢或者内存占用大 请先阅读[10M以上文件读取说明](/docs/LARGEREAD.md) -#### 详细参数介绍 -有些参数不知道怎么用,或者有些功能不知道用什么参数,参照:[详细参数介绍](/docs/API.md) -#### 开源项目不容易,如果觉得本项目对您的工作还是有帮助的话,请在右上角帮忙点个★Star。 -### 读 -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) -* [指定列的下标或者列名](#indexOrNameRead) -* [读多个或者全部sheet](#repeatedRead) -* [日期、数字或者自定义格式转换](#converterRead) -* [多行头](#complexHeaderRead) -* [同步的返回](#synchronousRead) -* [读取表头数据](#headerRead) -* [数据转换等异常处理](#exceptionRead) -* [web中的读](#webRead) - -### 写 -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) -* [最简单的写](#simpleWrite) -* [指定写入的列](#indexWrite) -* [复杂头写入](#complexHeadWrite) -* [重复多次写入(包括不同sheet)](#repeatedWrite) -* [日期、数字或者自定义格式转换](#converterWrite) -* [图片导出](#imageWrite) -* [根据模板写入](#templateWrite) -* [列宽、行高](#widthAndHeightWrite) -* [自定义样式](#styleWrite) -* [合并单元格](#mergeWrite) -* [使用table去写入](#tableWrite) -* [动态头,实时生成头写入](#dynamicHeadWrite) -* [自动列宽(不太精确)](#longestMatchColumnWidthWrite) -* [自定义拦截器(下拉,超链接等上面几点都不符合但是要对单元格进行操作的参照这个)](#customHandlerWrite) -* [web中的写](#webWrite) - -### 填充 -DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java) -* [最简单的填充](#simpleFill) -* [填充列表](#listFill) -* [复杂的填充](#complexFill) -* [数据量大的复杂填充](#complexFillWithTable) -* [横向的填充](#horizontalFill) - -## 读excel样例 -### 最简单的读 -##### excel示例 -![img](img/readme/quickstart/read/demo.png) -##### 对象 -```java -@Data -public class DemoData { - private String string; - private Date date; - private Double doubleData; -} -``` -##### 监听器 -```java -// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 -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() { - // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 - // 写法1: - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 - EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); - - // 写法2: - fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build(); - ReadSheet readSheet = EasyExcel.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 - EasyExcel.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"; - // 读取全部sheet - // 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写 - EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll(); - - // 读取部分sheet - fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - ExcelReader excelReader = EasyExcel.read(fileName).build(); - // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener - ReadSheet readSheet1 = - EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - ReadSheet readSheet2 = - EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能 - excelReader.read(readSheet1, 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 - EasyExcel.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 - EasyExcel.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 = EasyExcel.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 = EasyExcel.read(fileName).sheet().doReadSync(); - for (Object obj : list) { - // 返回每条数据的键值对 表示所在的列 和所在列的值 - Map data = (Map)obj; - LOGGER.info("读取到数据:{}", JSON.toJSONString(data)); - } - } -``` - -### 读取表头数据 -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -参照:[对象](#simpleReadObject) -##### 监听器 -参照:[监听器](#simpleReadListener) -里面多了一个方法,只要重写invokeHeadMap方法即可 -```java - /** - * 这里会一行行的返回头 - * - * @param headMap - * @param context - */ - @Override - public void invokeHeadMap(Map headMap, AnalysisContext context) { - LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap)); - } -``` -##### 代码 -```java - /** - * 读取表头数据 - * - *

- * 1. 创建excel对应的实体对象 参照{@link DemoData} - *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener} - *

- * 3. 直接读即可 - */ - @Test - public void headerRead() { - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet - EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); - } -``` - -### 数据转换等异常处理 -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -```java -@Data -public class ExceptionDemoData { - /** - * 用日期去接字符串 肯定报错 - */ - private Date date; -} -``` -##### 监听器 -参照:[监听器](#simpleReadListener) -里面多了一个方法,只要重写onException方法即可 -```java - /** - * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。 - * - * @param exception - * @param context - * @throws Exception - */ - @Override - public void onException(Exception exception, AnalysisContext context) { - LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage()); - // 如果是某一个单元格的转换异常 能获取到具体行号 - // 如果要获取头的信息 配合invokeHeadMap使用 - if (exception instanceof ExcelDataConvertException) { - ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception; - LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(), - excelDataConvertException.getColumnIndex()); - } - } -``` -##### 代码 -```java - /** - * 数据转换等异常处理 - * - *

- * 1. 创建excel对应的实体对象 参照{@link ExceptionDemoData} - *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExceptionListener} - *

- * 3. 直接读即可 - */ - @Test - public void exceptionRead() { - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet - EasyExcel.read(fileName, ExceptionDemoData.class, new DemoExceptionListener()).sheet().doRead(); - } -``` - - -### web中的读 -##### 示例代码 -DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java) -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -参照:[对象](#simpleReadObject) 只是名字变了 -##### 监听器 -参照:[监听器](#simpleReadListener) 只是泛型变了 -##### 代码 -```java - /** - * 文件上传 - *

1. 创建excel对应的实体对象 参照{@link UploadData} - *

2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener} - *

3. 直接读即可 - */ - @PostMapping("upload") - @ResponseBody - public String upload(MultipartFile file) throws IOException { - EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener()).sheet().doRead(); - return "success"; - } -``` - -## 写excel样例 -### 通用数据生成 后面不会重复写 -```java - private List data() { - List list = new ArrayList(); - for (int i = 0; i < 10; i++) { - DemoData data = new DemoData(); - data.setString("字符串" + i); - data.setDate(new Date()); - data.setDoubleData(0.56); - list.add(data); - } - return list; - } -``` -### 最简单的写 -##### excel示例 -![img](img/readme/quickstart/write/simpleWrite.png) -##### 对象 -```java -@Data -public class DemoData { - @ExcelProperty("字符串标题") - private String string; - @ExcelProperty("日期标题") - private Date date; - @ExcelProperty("数字标题") - private Double doubleData; - /** - * 忽略这个字段 - */ - @ExcelIgnore - private String ignore; -} -``` -##### 代码 -```java - /** - * 最简单的写 - *

1. 创建excel对应的实体对象 参照{@link DemoData} - *

2. 直接写即可 - */ - @Test - public void simpleWrite() { - // 写法1 - String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - // 如果这里想使用03 则 传入excelType参数即可 - EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data()); - - // 写法2 - fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读 - ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); - WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); - excelWriter.write(data(), writeSheet); - /// 千万别忘记finish 会帮忙关闭流 - excelWriter.finish(); - } -``` - -### 指定写入的列 -##### excel示例 -![img](img/readme/quickstart/write/indexWrite.png) -##### 对象 -```java -@Data -public class IndexData { - @ExcelProperty(value = "字符串标题", index = 0) - private String string; - @ExcelProperty(value = "日期标题", index = 1) - private Date date; - /** - * 这里设置3 会导致第二列空的 - */ - @ExcelProperty(value = "数字标题", index = 3) - private Double doubleData; -} -``` -##### 代码 -```java - /** - * 指定写入的列 - *

1. 创建excel对应的实体对象 参照{@link IndexData} - *

2. 使用{@link ExcelProperty}注解指定写入的列 - *

3. 直接写即可 - */ - @Test - public void indexWrite() { - String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, IndexData.class).sheet("模板").doWrite(data()); - } -``` - -### 复杂头写入 -##### excel示例 -![img](img/readme/quickstart/write/complexHeadWrite.png) -##### 对象 -```java -@Data -public class ComplexHeadData { - @ExcelProperty({"主标题", "字符串标题"}) - private String string; - @ExcelProperty({"主标题", "日期标题"}) - private Date date; - @ExcelProperty({"主标题", "数字标题"}) - private Double doubleData; -} -``` -##### 代码 -```java - /** - * 复杂头写入 - *

1. 创建excel对应的实体对象 参照{@link ComplexHeadData} - *

2. 使用{@link ExcelProperty}注解指定复杂的头 - *

3. 直接写即可 - */ - @Test - public void complexHeadWrite() { - String fileName = TestFileUtil.getPath() + "complexHeadWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, ComplexHeadData.class).sheet("模板").doWrite(data()); - } -``` - -### 重复多次写入 -##### excel示例 -![img](img/readme/quickstart/write/repeatedWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 重复多次写入 - *

- * 1. 创建excel对应的实体对象 参照{@link ComplexHeadData} - *

- * 2. 使用{@link ExcelProperty}注解指定复杂的头 - *

- * 3. 直接调用二次写入即可 - */ - @Test - public void repeatedWrite() { - // 方法1 如果写到同一个sheet - String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读 - ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); - // 这里注意 如果同一个sheet只要创建一次 - WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); - // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来 - for (int i = 0; i < 5; i++) { - // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 - List data = data(); - excelWriter.write(data, writeSheet); - } - /// 千万别忘记finish 会帮忙关闭流 - excelWriter.finish(); - - // 方法2 如果写到不同的sheet 同一个对象 - fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 指定文件 - excelWriter = EasyExcel.write(fileName, DemoData.class).build(); - // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面 - for (int i = 0; i < 5; i++) { - // 每次都要创建writeSheet 这里注意必须指定sheetNo - writeSheet = EasyExcel.writerSheet(i, "模板").build(); - // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 - List data = data(); - excelWriter.write(data, writeSheet); - } - /// 千万别忘记finish 会帮忙关闭流 - excelWriter.finish(); - - // 方法3 如果写到不同的sheet 不同的对象 - fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 指定文件 - excelWriter = EasyExcel.write(fileName).build(); - // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面 - for (int i = 0; i < 5; i++) { - // 每次都要创建writeSheet 这里注意必须指定sheetNo。这里注意DemoData.class 可以每次都变,我这里为了方便 所以用的同一个class 实际上可以一直变 - writeSheet = EasyExcel.writerSheet(i, "模板").head(DemoData.class).build(); - // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 - List data = data(); - excelWriter.write(data, writeSheet); - } - /// 千万别忘记finish 会帮忙关闭流 - excelWriter.finish(); - } -``` - -### 日期、数字或者自定义格式转换 -##### excel示例 -![img](img/readme/quickstart/write/converterWrite.png) -##### 对象 -```java -@Data -public class ConverterData { - /** - * 我想所有的 字符串起前面加上"自定义:"三个字 - */ - @ExcelProperty(value = "字符串标题", converter = CustomStringStringConverter.class) - private String string; - /** - * 我想写到excel 用年月日的格式 - */ - @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") - @ExcelProperty("日期标题") - private Date date; - /** - * 我想写到excel 用百分比表示 - */ - @NumberFormat("#.##%") - @ExcelProperty(value = "数字标题") - private Double doubleData; -} -``` -##### 代码 -```java - /** - * 日期、数字或者自定义格式转换 - *

1. 创建excel对应的实体对象 参照{@link ConverterData} - *

2. 使用{@link ExcelProperty}配合使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解 - *

3. 直接写即可 - */ - @Test - public void converterWrite() { - String fileName = TestFileUtil.getPath() + "converterWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, ConverterData.class).sheet("模板").doWrite(data()); - } -``` - -### 图片导出 -##### excel示例 -![img](img/readme/quickstart/write/imageWrite.png) -##### 对象 -```java -@Data -@ContentRowHeight(100) -@ColumnWidth(100 / 8) -public class ImageData { - private File file; - private InputStream inputStream; - /** - * 如果string类型 必须指定转换器,string默认转换成string - */ - @ExcelProperty(converter = StringImageConverter.class) - private String string; - private byte[] byteArray; -} -``` -##### 代码 -```java - /** - * 图片导出 - *

1. 创建excel对应的实体对象 参照{@link ImageData} - *

2. 直接写即可 - */ - @Test - public void imageWrite() throws Exception { - String fileName = TestFileUtil.getPath() + "imageWrite" + System.currentTimeMillis() + ".xlsx"; - // 如果使用流 记得关闭 - InputStream inputStream = null; - try { - List list = new ArrayList(); - ImageData imageData = new ImageData(); - list.add(imageData); - String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg"; - // 放入四种类型的图片 实际使用只要选一种即可 - imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath))); - imageData.setFile(new File(imagePath)); - imageData.setString(imagePath); - inputStream = FileUtils.openInputStream(new File(imagePath)); - imageData.setInputStream(inputStream); - EasyExcel.write(fileName, ImageData.class).sheet().doWrite(list); - } finally { - if (inputStream != null) { - inputStream.close(); - } - } - } -``` - - -### 根据模板写入 -##### 模板excel示例 -参照:[模板excel示例](#simpleReadExcel) -##### excel示例 -![img](img/readme/quickstart/write/templateWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 根据模板写入 - *

1. 创建excel对应的实体对象 参照{@link IndexData} - *

2. 使用{@link ExcelProperty}注解指定写入的列 - *

3. 使用withTemplate 读取模板 - *

4. 直接写即可 - */ - @Test - public void templateWrite() { - String templateFileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - String fileName = TestFileUtil.getPath() + "templateWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, DemoData.class).withTemplate(templateFileName).sheet().doWrite(data()); - } -``` - -### 列宽、行高 -##### excel示例 -![img](img/readme/quickstart/write/widthAndHeightWrite.png) -##### 对象 -````java -@Data -@ContentRowHeight(10) -@HeadRowHeight(20) -@ColumnWidth(25) -public class WidthAndHeightData { - @ExcelProperty("字符串标题") - private String string; - @ExcelProperty("日期标题") - private Date date; - /** - * 宽度为50 - */ - @ColumnWidth(50) - @ExcelProperty("数字标题") - private Double doubleData; -} -```` -##### 代码 -```java - /** - * 列宽、行高 - *

1. 创建excel对应的实体对象 参照{@link WidthAndHeightData} - *

2. 使用注解{@link ColumnWidth}、{@link HeadRowHeight}、{@link ContentRowHeight}指定宽度或高度 - *

3. 直接写即可 - */ - @Test - public void widthAndHeightWrite() { - String fileName = TestFileUtil.getPath() + "widthAndHeightWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, WidthAndHeightData.class).sheet("模板").doWrite(data()); - } -``` - -### 自定义样式 -##### excel示例 -![img](img/readme/quickstart/write/styleWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 自定义样式 - *

1. 创建excel对应的实体对象 参照{@link DemoData} - *

2. 创建一个style策略 并注册 - *

3. 直接写即可 - */ - @Test - public void styleWrite() { - String fileName = TestFileUtil.getPath() + "styleWrite" + System.currentTimeMillis() + ".xlsx"; - // 头的策略 - WriteCellStyle headWriteCellStyle = new WriteCellStyle(); - // 背景设置为红色 - headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); - WriteFont headWriteFont = new WriteFont(); - headWriteFont.setFontHeightInPoints((short)20); - headWriteCellStyle.setWriteFont(headWriteFont); - // 内容的策略 - WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); - // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 - contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); - // 背景绿色 - contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); - WriteFont contentWriteFont = new WriteFont(); - // 字体大小 - contentWriteFont.setFontHeightInPoints((short)20); - contentWriteCellStyle.setWriteFont(contentWriteFont); - // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 - HorizontalCellStyleStrategy horizontalCellStyleStrategy = - new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); - - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板") - .doWrite(data()); - } -``` - -### 合并单元格 -##### excel示例 -![img](img/readme/quickstart/write/mergeWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 合并单元格 - *

1. 创建excel对应的实体对象 参照{@link DemoData} - *

2. 创建一个merge策略 并注册 - *

3. 直接写即可 - */ - @Test - public void mergeWrite() { - String fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx"; - // 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写 - LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0); - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("模板") - .doWrite(data()); - } -``` - -### 使用table去写入 -##### excel示例 -![img](img/readme/quickstart/write/tableWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 使用table去写入 - *

1. 创建excel对应的实体对象 参照{@link DemoData} - *

2. 然后写入table即可 - */ - @Test - public void tableWrite() { - String fileName = TestFileUtil.getPath() + "tableWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里直接写多个table的案例了,如果只有一个 也可以直一行代码搞定,参照其他案例 - // 这里 需要指定写用哪个class去读 - ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); - // 把sheet设置为不需要头 不然会输出sheet的头 这样看起来第一个table 就有2个头了 - WriteSheet writeSheet = EasyExcel.writerSheet("模板").needHead(Boolean.FALSE).build(); - // 这里必须指定需要头,table 会继承sheet的配置,sheet配置了不需要,table 默认也是不需要 - WriteTable writeTable0 = EasyExcel.writerTable(0).needHead(Boolean.TRUE).build(); - WriteTable writeTable1 = EasyExcel.writerTable(1).needHead(Boolean.TRUE).build(); - // 第一次写入会创建头 - excelWriter.write(data(), writeSheet, writeTable0); - // 第二次写如也会创建头,然后在第一次的后面写入数据 - excelWriter.write(data(), writeSheet, writeTable1); - /// 千万别忘记finish 会帮忙关闭流 - excelWriter.finish(); - } -``` - -### 动态头,实时生成头写入 -##### excel示例 -![img](img/readme/quickstart/write/dynamicHeadWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 动态头,实时生成头写入 - *

- * 思路是这样子的,先创建List头格式的sheet仅仅写入头,然后通过table 不写入头的方式 去写入数据 - * - *

- * 1. 创建excel对应的实体对象 参照{@link DemoData} - *

- * 2. 然后写入table即可 - */ - @Test - public void dynamicHeadWrite() { - String fileName = TestFileUtil.getPath() + "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx"; - EasyExcel.write(fileName) - // 这里放入动态头 - .head(head()).sheet("模板") - // 当然这里数据也可以用 List> 去传入 - .doWrite(data()); - } - - private List> head() { - List> list = new ArrayList>(); - List head0 = new ArrayList(); - head0.add("字符串" + System.currentTimeMillis()); - List head1 = new ArrayList(); - head1.add("数字" + System.currentTimeMillis()); - List head2 = new ArrayList(); - head2.add("日期" + System.currentTimeMillis()); - list.add(head0); - list.add(head1); - list.add(head2); - return list; - } -``` - -### 自动列宽(不太精确) -##### excel示例 -![img](img/readme/quickstart/write/longestMatchColumnWidthWrite.png) -##### 对象 -```java -@Data -public class LongestMatchColumnWidthData { - @ExcelProperty("字符串标题") - private String string; - @ExcelProperty("日期标题很长日期标题很长日期标题很长很长") - private Date date; - @ExcelProperty("数字") - private Double doubleData; -} -``` -##### 代码 -```java - /** - * 自动列宽(不太精确) - *

- * 这个目前不是很好用,比如有数字就会导致换行。而且长度也不是刚好和实际长度一致。 所以需要精确到刚好列宽的慎用。 当然也可以自己参照 - * {@link LongestMatchColumnWidthStyleStrategy}重新实现. - *

- * poi 自带{@link SXSSFSheet#autoSizeColumn(int)} 对中文支持也不太好。目前没找到很好的算法。 有的话可以推荐下。 - * - *

- * 1. 创建excel对应的实体对象 参照{@link LongestMatchColumnWidthData} - *

- * 2. 注册策略{@link LongestMatchColumnWidthStyleStrategy} - *

- * 3. 直接写即可 - */ - @Test - public void longestMatchColumnWidthWrite() { - String fileName = - TestFileUtil.getPath() + "longestMatchColumnWidthWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, LongestMatchColumnWidthData.class) - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("模板").doWrite(dataLong()); - } - - private List dataLong() { - List list = new ArrayList(); - for (int i = 0; i < 10; i++) { - LongestMatchColumnWidthData data = new LongestMatchColumnWidthData(); - data.setString("测试很长的字符串测试很长的字符串测试很长的字符串" + i); - data.setDate(new Date()); - data.setDoubleData(1000000000000.0); - list.add(data); - } - return list; - } -``` - -### 自定义拦截器(上面几点都不符合但是要对单元格进行操作的参照这个) -##### excel示例 -![img](img/readme/quickstart/write/customHandlerWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 定义拦截器 -````java -/** - * 自定义拦截器。对第一行第一列的头超链接到:https://github.com/alibaba/easyexcel - * - * @author Jiaju Zhuang - */ -public class CustomCellWriteHandler implements CellWriteHandler { - - private static final Logger LOGGER = LoggerFactory.getLogger(CustomCellWriteHandler.class); - - @Override - public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { - - } - - @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, - Head head, Integer relativeRowIndex, Boolean isHead) { - - } - - @Override - public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, - List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { - // 这里可以对cell进行任何操作 - LOGGER.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex()); - if (isHead && cell.getColumnIndex() == 0) { - CreationHelper createHelper = writeSheetHolder.getSheet().getWorkbook().getCreationHelper(); - Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL); - hyperlink.setAddress("https://github.com/alibaba/easyexcel"); - cell.setHyperlink(hyperlink); - } - } - -} -```` -````java -/** - * 自定义拦截器.对第一列第一行和第二行的数据新增下拉框,显示 测试1 测试2 - * - * @author Jiaju Zhuang - */ -public class CustomSheetWriteHandler implements SheetWriteHandler { - - private static final Logger LOGGER = LoggerFactory.getLogger(CustomSheetWriteHandler.class); - - @Override - public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { - - } - - @Override - public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { - LOGGER.info("第{}个Sheet写入成功。", writeSheetHolder.getSheetNo()); - - // 区间设置 第一列第一行和第二行的数据。由于第一行是头,所以第一、二行的数据实际上是第二三行 - CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 2, 0, 0); - DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); - DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"测试1", "测试2"}); - DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList); - writeSheetHolder.getSheet().addValidationData(dataValidation); - } -} - -```` -##### 代码 -```java - /** - * 下拉,超链接等自定义拦截器(上面几点都不符合但是要对单元格进行操作的参照这个) - *

- * demo这里实现2点。1. 对第一行第一列的头超链接到:https://github.com/alibaba/easyexcel 2. 对第一列第一行和第二行的数据新增下拉框,显示 测试1 测试2 - *

- * 1. 创建excel对应的实体对象 参照{@link DemoData} - *

- * 2. 注册拦截器 {@link CustomCellWriteHandler} {@link CustomSheetWriteHandler} - *

- * 2. 直接写即可 - */ - @Test - public void customHandlerWrite() { - String fileName = TestFileUtil.getPath() + "customHandlerWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, DemoData.class).registerWriteHandler(new CustomSheetWriteHandler()) - .registerWriteHandler(new CustomCellWriteHandler()).sheet("模板").doWrite(data()); - } -``` - -### web中的写 -##### 示例代码 -DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java) -##### 对象 -参照:[对象](#simpleWriteObject) 就是名称变了下 -##### 代码 -```java - /** - * 文件下载 - *

- * 1. 创建excel对应的实体对象 参照{@link DownloadData} - *

- * 2. 设置返回的 参数 - *

- * 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大 - */ - @GetMapping("download") - public void download(HttpServletResponse response) throws IOException { - // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman - response.setContentType("application/vnd.ms-excel"); - response.setCharacterEncoding("utf-8"); - // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 - String fileName = URLEncoder.encode("测试", "UTF-8"); - response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); - EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data()); - } -``` - -## 填充excel样例 -### 最简单的填充 -##### 模板 -![img](img/readme/quickstart/fill/simpleFillTemplate.png) -##### 最终效果 -![img](img/readme/quickstart/fill/simpleFill.png) -##### 对象 -```java -@Data -public class FillData { - private String name; - private double number; -} -``` -##### 代码 -```java - /** - * 最简单的填充 - */ - @Test - public void simpleFill() { - // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 - String templateFileName = - TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx"; - - // 方案1 根据对象填充 - String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; - // 这里 会填充到第一个sheet, 然后文件流会自动关闭 - FillData fillData = new FillData(); - fillData.setName("张三"); - fillData.setNumber(5.2); - EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData); - - // 方案2 根据Map填充 - fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; - // 这里 会填充到第一个sheet, 然后文件流会自动关闭 - Map map = new HashMap(); - map.put("name", "张三"); - map.put("number", 5.2); - EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); - } -``` - -### 填充列表 -##### 模板 -![img](img/readme/quickstart/fill/listFillTemplate.png) -##### 最终效果 -![img](img/readme/quickstart/fill/listFill.png) -##### 对象 -参照:[对象](#simpleFillObject) -##### 代码 -```java - /** - * 填充列表 - */ - @Test - public void listFill() { - // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 - // 填充list 的时候还要注意 模板中{.} 多了个点 表示list - String templateFileName = - TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx"; - - // 方案1 一下子全部放到内存里面 并填充 - String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; - // 这里 会填充到第一个sheet, 然后文件流会自动关闭 - EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data()); - - // 方案2 分多次 填充 会使用文件缓存(省内存) - fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; - ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); - excelWriter.fill(data(), writeSheet); - excelWriter.fill(data(), writeSheet); - // 千万别忘记关闭流 - excelWriter.finish(); - } -``` - -### 复杂的填充 -##### 模板 -![img](img/readme/quickstart/fill/complexFillTemplate.png) -##### 最终效果 -![img](img/readme/quickstart/fill/complexFill.png) -##### 对象 -参照:[对象](#simpleFillObject) -##### 代码 -```java - /** - * 复杂的填充 - */ - @Test - public void complexFill() { - // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 - // {} 代表普通变量 {.} 代表是list的变量 - String templateFileName = - TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx"; - - String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; - ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); - // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 - // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 - // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 - // 如果数据量大 list不是最后一行 参照下一个 - FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); - excelWriter.fill(data(), fillConfig, writeSheet); - excelWriter.fill(data(), fillConfig, writeSheet); - Map map = new HashMap(); - map.put("date", "2019年10月9日13:28:28"); - map.put("total", 1000); - excelWriter.fill(map, writeSheet); - excelWriter.finish(); - } -``` - -### 数据量大的复杂填充 -##### 模板 -![img](img/readme/quickstart/fill/complexFillWithTableTemplate.png) -##### 最终效果 -![img](img/readme/quickstart/fill/complexFillWithTable.png) -##### 对象 -参照:[对象](#simpleFillObject) -##### 代码 -```java - /** - * 数据量大的复杂填充 - *

- * 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。 - */ - @Test - public void complexFillWithTable() { - // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 - // {} 代表普通变量 {.} 代表是list的变量 - // 这里模板 删除了list以后的数据,也就是统计的这一行 - String templateFileName = - TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complexFillWithTable.xlsx"; - - String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx"; - ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); - // 直接写入数据 - excelWriter.fill(data(), writeSheet); - excelWriter.fill(data(), writeSheet); - - // 写入list之前的数据 - Map map = new HashMap(); - map.put("date", "2019年10月9日13:28:28"); - excelWriter.fill(map, writeSheet); - - // list 后面还有个统计 想办法手动写入 - // 这里偷懒直接用list 也可以用对象 - List> totalListList = new ArrayList>(); - List totalList = new ArrayList(); - totalListList.add(totalList); - totalList.add(null); - totalList.add(null); - totalList.add(null); - // 第四列 - totalList.add("统计:1000"); - // 这里是write 别和fill 搞错了 - excelWriter.write(totalListList, writeSheet); - excelWriter.finish(); - // 总体上写法比较复杂 但是也没有想到好的版本 异步的去写入excel 不支持行的删除和移动,也不支持备注这种的写入,所以也排除了可以 - // 新建一个 然后一点点复制过来的方案,最后导致list需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案 - } -``` - -### 横向的填充 -##### 模板 -![img](img/readme/quickstart/fill/horizontalFillTemplate.png) -##### 最终效果 -![img](img/readme/quickstart/fill/horizontalFill.png) -##### 对象 -参照:[对象](#simpleFillObject) -##### 代码 -```java - /** - * 横向的填充 - */ - @Test - public void horizontalFill() { - // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 - // {} 代表普通变量 {.} 代表是list的变量 - String templateFileName = - TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "horizontal.xlsx"; - - String fileName = TestFileUtil.getPath() + "horizontalFill" + System.currentTimeMillis() + ".xlsx"; - ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); - FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); - excelWriter.fill(data(), fillConfig, writeSheet); - excelWriter.fill(data(), fillConfig, writeSheet); - - Map map = new HashMap(); - map.put("date", "2019年10月9日13:28:28"); - excelWriter.fill(map, writeSheet); - - // 别忘记关闭流 - excelWriter.finish(); - } -``` - -## 测试数据分析 -![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个人再用估计一台机器就挂了。 - +# 请登录官网查看 +官网地址:[https://alibaba-easyexcel.github.io](https://alibaba-easyexcel.github.io) \ No newline at end of file diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java index 3b4754d7..6e1f435d 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -60,7 +60,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { } switch (excelType) { case XLS: - POIFSFileSystem poifsFileSystem = null; + POIFSFileSystem poifsFileSystem; if (readWorkbookHolder.getFile() != null) { poifsFileSystem = new POIFSFileSystem(readWorkbookHolder.getFile()); } else { diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java index a94afb3c..35b38592 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java @@ -141,11 +141,11 @@ public class ReadWorkbookHolder extends AbstractReadHolder { } else { this.autoCloseStream = readWorkbook.getAutoCloseStream(); } - if (readWorkbook.getExcelType() == null) { - this.excelType = ExcelTypeEnum.valueOf(file, inputStream); - } else { - this.excelType = readWorkbook.getExcelType(); - } + + // The type of excel is read according to the judgment.Because encrypted XLSX needs to be specified as XLS to + // properly parse. + this.excelType = ExcelTypeEnum.valueOf(file, inputStream, readWorkbook.getExcelType()); + if (ExcelTypeEnum.XLS == excelType && getGlobalConfiguration().getUse1904windowing() == null) { getGlobalConfiguration().setUse1904windowing(Boolean.FALSE); } diff --git a/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java b/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java index 2f02327c..b4e41dff 100644 --- a/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java +++ b/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java @@ -29,7 +29,7 @@ public enum ExcelTypeEnum { this.setValue(value); } - public static ExcelTypeEnum valueOf(File file, InputStream inputStream) { + public static ExcelTypeEnum valueOf(File file, InputStream inputStream, ExcelTypeEnum excelType) { try { FileMagic fileMagic; if (file != null) { @@ -59,9 +59,15 @@ public enum ExcelTypeEnum { return XLSX; } } catch (IOException e) { + if (excelType != null) { + return excelType; + } throw new ExcelCommonException( "Convert excel format exception.You can try specifying the 'excelType' yourself", e); } + if (excelType != null) { + return excelType; + } throw new ExcelCommonException( "Convert excel format exception.You can try specifying the 'excelType' yourself"); } diff --git a/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java b/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java index ae6c4952..7d9ebcd6 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java +++ b/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java @@ -43,7 +43,7 @@ public class WriteWorkbook extends WriteBasicParameter { */ private File templateFile; /** - * Default true + * Default trueuseDefaultStyle */ private Boolean autoCloseStream; /** diff --git a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java index 7477887c..98f580f8 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java @@ -57,7 +57,7 @@ public class EncryptDataTest { @Test public void t02ReadAndWriteStream03() throws Exception { - readAndWriteStream(file03OutputStream, ExcelTypeEnum.XLSX); + readAndWriteStream(file03OutputStream, ExcelTypeEnum.XLS); } private void readAndWrite(File file) { @@ -72,8 +72,8 @@ public class EncryptDataTest { fileOutputStream.close(); FileInputStream fileInputStream = new FileInputStream(file); - EasyExcel.read(fileInputStream, EncryptData.class, new EncryptDataListener()).password("123456").sheet() - .doRead(); + EasyExcel.read(fileInputStream, EncryptData.class, new EncryptDataListener()).password("123456") + .excelType(excelType).sheet().doRead(); } private List data() { diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java b/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java index cba9bae0..052b60b6 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java @@ -29,7 +29,7 @@ public class Lock2Test { @Test public void test() throws Exception { - File file = new File("D:\\test\\test001.xlsx"); + File file = new File("D:\\test\\000001.xlsx"); List list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync(); LOGGER.info("数据:{}", list.size()); diff --git a/update.md b/update.md index 331dfbe8..2ae7d28f 100644 --- a/update.md +++ b/update.md @@ -2,7 +2,7 @@ * 新增支持导入、导出支持公式 * 新增支持读取单元格类型、写入指定单元格类型 * 支持通过模板填充数据 -* 新增写支持 禁用头样式 `autoHeadStyle` +* 新增写支持 禁用头样式 `useDefaultStyle` * 用map读取数据 空的单元格也会有个 null的数据 * 转换报错 能获取到对应的行号和列号 * 优化读取全部sheet方案