diff --git a/README.md b/README.md index a04267e2..fd5c5a2f 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,21 @@ 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) +[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模式。在上层做了模型转换的封装,让使用者更加简单方便 + +## 64M内存1分钟内读取75M(46W行25列)的Excel +当然还有急速模式能更快,但是内存占用会在100M多一点 +![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/abouteasyexcel.md b/abouteasyexcel.md index aa6bfa31..54a4e1fd 100644 --- a/abouteasyexcel.md +++ b/abouteasyexcel.md @@ -50,4 +50,4 @@ Caused by: java.io.IOException: Could not create temporary directory '/home/admi ### 3、抛弃不重要的数据 -Excel解析时候会包含样式,字体,宽度等数据,但这些数据是我们不关心的,如果将这部分数据抛弃可以大大降低内存使用。Excel中数据如下Style占了相当大的空间。 +Excel解析时候会包含样式,字体,宽度等数据,但这些数据是我们不关心的,如果将这部分数据抛弃可以大大降低内存使用。Excel中数据如下Style占了相当大的空间。 \ No newline at end of file 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/docs/LARGEREAD.md b/docs/LARGEREAD.md index 512596d4..86147277 100644 --- a/docs/LARGEREAD.md +++ b/docs/LARGEREAD.md @@ -9,6 +9,7 @@ ```java // 强制使用内存存储,这样大概一个20M的excel使用150M(很多临时对象,所以100M会一直GC)的内存 // 这样效率会比上面的复杂的策略高很多 + // 这里再说明下 就是加了个readCache(new MapCache()) 参数而已,其他的参照其他demo写 这里没有写全 EasyExcel.read().readCache(new MapCache()); ``` ### 对并发要求较高,而且都是经常有超级大文件 @@ -16,7 +17,10 @@ // 第一个参数的意思是 多少M共享字符串以后 采用文件存储 单位MB 默认5M // 第二个参数 文件存储时,内存存放多少M缓存数据 默认20M // 比如 你希望用100M内存(这里说的是解析过程中的永久占用,临时对象不算)来解析excel,前面算过了 大概是 20M+90M 所以设置参数为:20 和 90 + // 这里再说明下 就是加了个readCacheSelector(new SimpleReadCacheSelector(5, 20))参数而已,其他的参照其他demo写 这里没有写全 EasyExcel.read().readCacheSelector(new SimpleReadCacheSelector(5, 20)); ``` ### 关于maxCacheActivateSize 也就是前面第二个参数的详细说明 -easyexcel在使用文件存储的时候,会把共享字符串拆分成1000条一批,然后放到文件存储。然后excel来读取共享字符串大概率是按照顺序的,所以默认20M的1000条的数据放在内存,命中后直接返回,没命中去读文件。所以不能设置太小,太小了,很难命中,一直去读取文件,太大了的话会占用过多的内存。 \ No newline at end of file +easyexcel在使用文件存储的时候,会把共享字符串拆分成1000条一批,然后放到文件存储。然后excel来读取共享字符串大概率是按照顺序的,所以默认20M的1000条的数据放在内存,命中后直接返回,没命中去读文件。所以不能设置太小,太小了,很难命中,一直去读取文件,太大了的话会占用过多的内存。 +### 如何判断 maxCacheActivateSize是否需要调整 +开启debug日志会输出`Already put :4000000` 最后一次输出,大概可以得出值为400W,然后看`Cache misses count:4001`得到值为4K,400W/4K=1000 这代表已经`maxCacheActivateSize` 已经非常合理了。如果小于500 问题就非常大了,500到1000 应该都还行。 diff --git a/img/readme/large.png b/img/readme/large.png new file mode 100644 index 00000000..04195a10 Binary files /dev/null and b/img/readme/large.png differ diff --git a/img/readme/quickstart/fill/complexFill.png b/img/readme/quickstart/fill/complexFill.png new file mode 100644 index 00000000..d37bb0f9 Binary files /dev/null and b/img/readme/quickstart/fill/complexFill.png differ diff --git a/img/readme/quickstart/fill/complexFillTemplate.png b/img/readme/quickstart/fill/complexFillTemplate.png new file mode 100644 index 00000000..14e26b95 Binary files /dev/null and b/img/readme/quickstart/fill/complexFillTemplate.png differ diff --git a/img/readme/quickstart/fill/complexFillWithTable.png b/img/readme/quickstart/fill/complexFillWithTable.png new file mode 100644 index 00000000..c1075201 Binary files /dev/null and b/img/readme/quickstart/fill/complexFillWithTable.png differ diff --git a/img/readme/quickstart/fill/complexFillWithTableTemplate.png b/img/readme/quickstart/fill/complexFillWithTableTemplate.png new file mode 100644 index 00000000..902cd0da Binary files /dev/null and b/img/readme/quickstart/fill/complexFillWithTableTemplate.png differ diff --git a/img/readme/quickstart/fill/horizontalFill.png b/img/readme/quickstart/fill/horizontalFill.png new file mode 100644 index 00000000..10f90cd7 Binary files /dev/null and b/img/readme/quickstart/fill/horizontalFill.png differ diff --git a/img/readme/quickstart/fill/horizontalFillTemplate.png b/img/readme/quickstart/fill/horizontalFillTemplate.png new file mode 100644 index 00000000..462b86eb Binary files /dev/null and b/img/readme/quickstart/fill/horizontalFillTemplate.png differ diff --git a/img/readme/quickstart/fill/listFill.png b/img/readme/quickstart/fill/listFill.png new file mode 100644 index 00000000..3baca5a8 Binary files /dev/null and b/img/readme/quickstart/fill/listFill.png differ diff --git a/img/readme/quickstart/fill/listFillTemplate.png b/img/readme/quickstart/fill/listFillTemplate.png new file mode 100644 index 00000000..010c8c16 Binary files /dev/null and b/img/readme/quickstart/fill/listFillTemplate.png differ diff --git a/img/readme/quickstart/fill/simpleFill.png b/img/readme/quickstart/fill/simpleFill.png new file mode 100644 index 00000000..4f575628 Binary files /dev/null and b/img/readme/quickstart/fill/simpleFill.png differ diff --git a/img/readme/quickstart/fill/simpleFillTemplate.png b/img/readme/quickstart/fill/simpleFillTemplate.png new file mode 100644 index 00000000..97a9119d Binary files /dev/null and b/img/readme/quickstart/fill/simpleFillTemplate.png differ diff --git a/pom.xml b/pom.xml index 8777c67a..68fe674a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.alibaba easyexcel - 2.0.5 + 2.1.0-beta1 jar easyexcel 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 11f6eb10..dcf12add 100644 --- a/quickstart.md +++ b/quickstart.md @@ -1,1128 +1,2 @@ -# easyexcel核心功能 -## 各位读取文件务必使用2.0.3起 -2.0.0-beta1到2.0.2有小概率会丢失数字。 -## 目录 -### 前言 -#### 以下功能目前不支持 -* 单个文件的并发写入、读取 -* 读取图片 -* 宏 -ClassNotFoundException与java.lang.NoClassDefFoundError的区别 -* 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) - -## 读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 文件流会自动关闭 - 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() { - // 方法1 如果 sheet1 sheet2 都是同一数据 监听器和头 都写到最外层 - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build(); - ReadSheet readSheet1 = EasyExcel.readSheet(0).build(); - ReadSheet readSheet2 = EasyExcel.readSheet(1).build(); - excelReader.read(readSheet1); - excelReader.read(readSheet2); - // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 - excelReader.finish(); - - // 方法2 如果 sheet1 sheet2 数据不一致的话 - fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - excelReader = EasyExcel.read(fileName).build(); - // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener - readSheet1 = EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - readSheet2 = EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).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 - 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 然后千万别忘记 finish - 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 然后千万别忘记 finish - EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); - } -``` - -### 数据转换等异常处理 -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -参照:[对象](#simpleReadObject) -##### 监听器 -参照:[监听器](#simpleReadListener) -里面多了一个方法,只要重写onException方法即可 -```java - /** - * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。 - * - * @param exception - * @param context - * @throws Exception - */ - @Override - public void onException(Exception exception, AnalysisContext context) { - LOGGER.error("解析失败,但是继续解析下一行", exception); - } -``` -##### 代码 -```java - /** - * 数据转换等异常处理 - * - *

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

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

- * 3. 直接读即可 - */ - @Test - public void exceptionRead() { - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish - EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).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; -} -``` -##### 代码 -```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, int relativeRowIndex, boolean isHead) { - - } - - @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int 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 他会有影响 - 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()); - } -``` -## 测试数据分析 -![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/EasyExcelFactory.java b/src/main/java/com/alibaba/excel/EasyExcelFactory.java index 39fe0311..a4924973 100644 --- a/src/main/java/com/alibaba/excel/EasyExcelFactory.java +++ b/src/main/java/com/alibaba/excel/EasyExcelFactory.java @@ -73,7 +73,7 @@ public class EasyExcelFactory { * read sheet. * @param listener * Callback method after each row is parsed. - * @deprecated please use 'EasyExcel.read(in,head,listener).sheet(sheetNo).doRead().finish();' + * @deprecated please use 'EasyExcel.read(in,head,listener).sheet(sheetNo).doRead();' */ @Deprecated public static void readBySax(InputStream in, Sheet sheet, AnalysisEventListener listener) { diff --git a/src/main/java/com/alibaba/excel/ExcelReader.java b/src/main/java/com/alibaba/excel/ExcelReader.java index 3cb6b01e..46f25877 100644 --- a/src/main/java/com/alibaba/excel/ExcelReader.java +++ b/src/main/java/com/alibaba/excel/ExcelReader.java @@ -2,6 +2,7 @@ package com.alibaba.excel; import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.slf4j.Logger; @@ -9,7 +10,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.excel.analysis.ExcelAnalyser; import com.alibaba.excel.analysis.ExcelAnalyserImpl; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.cache.MapCache; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; @@ -147,27 +148,41 @@ public class ExcelReader { /** * Parse all sheet content by default + * + * @deprecated lease use {@link #readAll()} */ + @Deprecated public void read() { - ExcelExecutor excelExecutor = excelAnalyser.excelExecutor(); - if (excelExecutor.sheetList().isEmpty()) { - LOGGER.warn("Excel doesn't have any sheets."); - return; - } - for (ReadSheet readSheet : excelExecutor.sheetList()) { - read(readSheet); - } + readAll(); + } + + /*** + * Parse all sheet content by default + */ + public void readAll() { + checkFinished(); + excelAnalyser.analysis(null, Boolean.TRUE); } /** - * Parse the specified sheet,SheetNo start from 1 + * Parse the specified sheet,SheetNo start from 0 * * @param readSheet * Read sheet */ - public ExcelReader read(ReadSheet readSheet) { + public ExcelReader read(ReadSheet... readSheet) { + return read(Arrays.asList(readSheet)); + } + + /** + * Read multiple sheets. + * + * @param readSheetList + * @return + */ + public ExcelReader read(List readSheetList) { checkFinished(); - excelAnalyser.analysis(readSheet); + excelAnalyser.analysis(readSheetList, Boolean.FALSE); return this; } @@ -225,7 +240,7 @@ public class ExcelReader { * * @return */ - public ExcelExecutor excelExecutor() { + public ExcelReadExecutor excelExecutor() { checkFinished(); return excelAnalyser.excelExecutor(); } diff --git a/src/main/java/com/alibaba/excel/ExcelWriter.java b/src/main/java/com/alibaba/excel/ExcelWriter.java index e669b56e..65a99fe5 100644 --- a/src/main/java/com/alibaba/excel/ExcelWriter.java +++ b/src/main/java/com/alibaba/excel/ExcelWriter.java @@ -17,6 +17,7 @@ import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteWorkbook; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * Excel Writer This tool is used to write value out to Excel via POI. This object can perform the following two @@ -156,6 +157,30 @@ public class ExcelWriter { return this; } + /** + * Fill value to a sheet + * + * @param data + * @param writeSheet + * @return + */ + public ExcelWriter fill(Object data, WriteSheet writeSheet) { + return fill(data, null, writeSheet); + } + + /** + * Fill value to a sheet + * + * @param data + * @param fillConfig + * @param writeSheet + * @return + */ + public ExcelWriter fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { + excelBuilder.fill(data, fillConfig, writeSheet); + return this; + } + /** * Write data to a sheet * diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java index 8d987eaf..83de6ac5 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java @@ -1,5 +1,7 @@ package com.alibaba.excel.analysis; +import java.util.List; + import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.read.metadata.ReadSheet; @@ -10,12 +12,14 @@ import com.alibaba.excel.read.metadata.ReadSheet; */ public interface ExcelAnalyser { /** - * parse one sheet + * parse the sheet * - * @param readSheet - * sheet to read + * @param readSheetList + * Which sheets you need to read. + * @param readAll + * The readSheetList parameter is ignored, and all sheets are read. */ - void analysis(ReadSheet readSheet); + void analysis(List readSheetList, Boolean readAll); /** * Complete the entire read file.Release the cache and close stream @@ -27,7 +31,7 @@ public interface ExcelAnalyser { * * @return Excel file Executor */ - ExcelExecutor excelExecutor(); + ExcelReadExecutor excelExecutor(); /** * get the analysis context. diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java index cf2dff97..6e1f435d 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -1,8 +1,13 @@ package com.alibaba.excel.analysis; +import java.io.FileInputStream; import java.io.InputStream; +import java.util.List; +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.poifs.crypt.Decryptor; +import org.apache.poi.poifs.crypt.EncryptionInfo; import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.IOUtils; @@ -19,7 +24,9 @@ import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.ReadWorkbook; import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.StringUtils; /** * @author jipengfei @@ -29,7 +36,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { private AnalysisContext analysisContext; - private ExcelExecutor excelExecutor; + private ExcelReadExecutor excelReadExecutor; public ExcelAnalyserImpl(ReadWorkbook readWorkbook) { try { @@ -48,12 +55,12 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); ExcelTypeEnum excelType = readWorkbookHolder.getExcelType(); if (excelType == null) { - excelExecutor = new XlsxSaxAnalyser(analysisContext, null); + excelReadExecutor = new XlsxSaxAnalyser(analysisContext, null); return; } switch (excelType) { case XLS: - POIFSFileSystem poifsFileSystem = null; + POIFSFileSystem poifsFileSystem; if (readWorkbookHolder.getFile() != null) { poifsFileSystem = new POIFSFileSystem(readWorkbookHolder.getFile()); } else { @@ -64,8 +71,9 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { InputStream decryptedStream = null; try { decryptedStream = - DocumentFactoryHelper.getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), null); - excelExecutor = new XlsxSaxAnalyser(analysisContext, decryptedStream); + DocumentFactoryHelper.getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), + analysisContext.readWorkbookHolder().getPassword()); + excelReadExecutor = new XlsxSaxAnalyser(analysisContext, decryptedStream); return; } finally { IOUtils.closeQuietly(decryptedStream); @@ -74,27 +82,37 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { poifsFileSystem.close(); } } - excelExecutor = new XlsSaxAnalyser(analysisContext, poifsFileSystem); + if (analysisContext.readWorkbookHolder().getPassword() != null) { + Biff8EncryptionKey.setCurrentUserPassword(analysisContext.readWorkbookHolder().getPassword()); + } + excelReadExecutor = new XlsSaxAnalyser(analysisContext, poifsFileSystem); break; case XLSX: - excelExecutor = new XlsxSaxAnalyser(analysisContext, null); + excelReadExecutor = new XlsxSaxAnalyser(analysisContext, null); break; default: } } @Override - public void analysis(ReadSheet readSheet) { + public void analysis(List readSheetList, Boolean readAll) { try { - analysisContext.currentSheet(excelExecutor, readSheet); + if (!readAll && CollectionUtils.isEmpty(readSheetList)) { + throw new IllegalArgumentException("Specify at least one read sheet."); + } try { - excelExecutor.execute(); + excelReadExecutor.execute(readSheetList, readAll); } catch (ExcelAnalysisStopException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Custom stop!"); } } - analysisContext.readSheetHolder().notifyAfterAllAnalysed(analysisContext); + // The last sheet is read + if (excelReadExecutor instanceof XlsSaxAnalyser) { + if (analysisContext.readSheetHolder() != null) { + analysisContext.readSheetHolder().notifyAfterAllAnalysed(analysisContext); + } + } } catch (RuntimeException e) { finish(); throw e; @@ -110,6 +128,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { return; } ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); + try { if (readWorkbookHolder.getReadCache() != null) { readWorkbookHolder.getReadCache().destroy(); @@ -146,6 +165,16 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { } catch (Throwable t) { throwCanNotCloseIo(t); } + + clearEncrypt03(); + } + + private void clearEncrypt03() { + if (StringUtils.isEmpty(analysisContext.readWorkbookHolder().getPassword()) + || !ExcelTypeEnum.XLS.equals(analysisContext.readWorkbookHolder().getExcelType())) { + return; + } + Biff8EncryptionKey.setCurrentUserPassword(null); } private void throwCanNotCloseIo(Throwable t) { @@ -153,8 +182,8 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { } @Override - public com.alibaba.excel.analysis.ExcelExecutor excelExecutor() { - return excelExecutor; + public ExcelReadExecutor excelExecutor() { + return excelReadExecutor; } @Override diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelExecutor.java b/src/main/java/com/alibaba/excel/analysis/ExcelExecutor.java deleted file mode 100644 index 8868d2ee..00000000 --- a/src/main/java/com/alibaba/excel/analysis/ExcelExecutor.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alibaba.excel.analysis; - -import java.util.List; - -import com.alibaba.excel.read.metadata.ReadSheet; - -/** - * Excel file Executor - * - * @author Jiaju Zhuang - */ -public interface ExcelExecutor { - - /** - * Returns the actual sheet in excel - * - * @return Actual sheet in excel - */ - List sheetList(); - - /** - * Read sheet - */ - void execute(); - -} diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java b/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java new file mode 100644 index 00000000..10ced98d --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.analysis; + +import java.util.List; + +import com.alibaba.excel.read.metadata.ReadSheet; + +/** + * Excel file Executor + * + * @author Jiaju Zhuang + */ +public interface ExcelReadExecutor { + + /** + * Returns the actual sheet in excel + * + * @return Actual sheet in excel + */ + List sheetList(); + + /** + * Read the sheet. + * + * @param readSheetList + * Which sheets you need to read. + * @param readAll + * The readSheetList parameter is ignored, and all sheets are read. + */ + void execute(List readSheetList, Boolean readAll); +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java index 62d55175..6f8e7161 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java @@ -10,7 +10,6 @@ import org.apache.poi.hssf.eventusermodel.HSSFEventFactory; import org.apache.poi.hssf.eventusermodel.HSSFListener; import org.apache.poi.hssf.eventusermodel.HSSFRequest; import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener; -import org.apache.poi.hssf.record.BoundSheetRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.poifs.filesystem.POIFSFileSystem; @@ -27,13 +26,14 @@ import com.alibaba.excel.read.metadata.ReadSheet; public class XlsListSheetListener implements HSSFListener { private POIFSFileSystem poifsFileSystem; private List sheetList; - private XlsRecordHandler bofRecordHandler; + private BofRecordHandler bofRecordHandler; public XlsListSheetListener(AnalysisContext analysisContext, POIFSFileSystem poifsFileSystem) { this.poifsFileSystem = poifsFileSystem; sheetList = new ArrayList(); - bofRecordHandler = new BofRecordHandler(analysisContext, sheetList, false); + bofRecordHandler = new BofRecordHandler(analysisContext, sheetList, false, false); bofRecordHandler.init(); + bofRecordHandler.init(null, true); } @Override diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java index 36d2b74b..21977a4d 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java @@ -22,7 +22,7 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.analysis.v03.handlers.BlankOrErrorRecordHandler; import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler; import com.alibaba.excel.analysis.v03.handlers.FormulaRecordHandler; @@ -56,10 +56,12 @@ import com.alibaba.excel.util.CollectionUtils; * * @author jipengfei */ -public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor { +public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { private static final Logger LOGGER = LoggerFactory.getLogger(XlsSaxAnalyser.class); private POIFSFileSystem poifsFileSystem; + private Boolean readAll; + private List readSheetList; private int lastRowNumber; private int lastColumnNumber; /** @@ -91,8 +93,9 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor { } @Override - public void execute() { - analysisContext.readSheetHolder().getSheetNo(); + public void execute(List readSheetList, Boolean readAll) { + this.readAll = readAll; + this.readSheetList = readSheetList; MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this); formatListener = new FormatTrackingHSSFListener(listener); workbookBuildingListener = new EventWorkbookBuilder.SheetRecordCollectingListener(formatListener); @@ -196,9 +199,9 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor { // The table has been counted and there are no duplicate statistics if (sheets == null) { sheets = new ArrayList(); - recordHandlers.add(new BofRecordHandler(analysisContext, sheets, false)); + recordHandlers.add(new BofRecordHandler(analysisContext, sheets, false, true)); } else { - recordHandlers.add(new BofRecordHandler(analysisContext, sheets, true)); + recordHandlers.add(new BofRecordHandler(analysisContext, sheets, true, true)); } recordHandlers.add(new FormulaRecordHandler(stubWorkbook, formatListener)); recordHandlers.add(new LabelRecordHandler()); @@ -212,6 +215,10 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor { for (XlsRecordHandler x : recordHandlers) { x.init(); + if (x instanceof BofRecordHandler) { + BofRecordHandler bofRecordHandler = (BofRecordHandler)x; + bofRecordHandler.init(readSheetList, readAll); + } } } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java index d6a93a5a..3a862942 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java @@ -10,6 +10,7 @@ import org.apache.poi.hssf.record.Record; import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.util.SheetUtils; /** * Record handler @@ -21,13 +22,18 @@ public class BofRecordHandler extends AbstractXlsRecordHandler { private BoundSheetRecord[] orderedBsrs; private int sheetIndex; private List sheets; + private Boolean readAll; + private List readSheetList; private AnalysisContext context; private boolean alreadyInit; + private boolean needInitSheet; - public BofRecordHandler(AnalysisContext context, List sheets, boolean alreadyInit) { + public BofRecordHandler(AnalysisContext context, List sheets, boolean alreadyInit, + boolean needInitSheet) { this.context = context; this.sheets = sheets; this.alreadyInit = alreadyInit; + this.needInitSheet = needInitSheet; } @Override @@ -45,15 +51,34 @@ public class BofRecordHandler extends AbstractXlsRecordHandler { if (orderedBsrs == null) { orderedBsrs = BoundSheetRecord.orderByBofPosition(boundSheetRecords); } + String sheetName = orderedBsrs[sheetIndex].getSheetname(); + // Find the currently read sheet + ReadSheet readSheet = null; if (!alreadyInit) { - ReadSheet readSheet = new ReadSheet(sheetIndex, orderedBsrs[sheetIndex].getSheetname()); + readSheet = new ReadSheet(sheetIndex, sheetName); sheets.add(readSheet); } - if (context.readSheetHolder() != null) { - if (sheetIndex == context.readSheetHolder().getSheetNo()) { + if (needInitSheet) { + if (readSheet == null) { + for (ReadSheet sheet : sheets) { + if (sheet.getSheetNo() == sheetIndex) { + readSheet = sheet; + break; + } + } + } + assert readSheet != null : "Can't find the sheet."; + context.readWorkbookHolder().setIgnoreRecord03(Boolean.TRUE); + // Copy the parameter to the current sheet + readSheet = SheetUtils.match(readSheet, readSheetList, readAll, + context.readWorkbookHolder().getGlobalConfiguration()); + if (readSheet != null) { + if (readSheet.getSheetNo() != 0) { + // Prompt for the end of the previous form read + context.readSheetHolder().notifyAfterAllAnalysed(context); + } + context.currentSheet(readSheet); context.readWorkbookHolder().setIgnoreRecord03(Boolean.FALSE); - } else { - context.readWorkbookHolder().setIgnoreRecord03(Boolean.TRUE); } } sheetIndex++; @@ -71,6 +96,11 @@ public class BofRecordHandler extends AbstractXlsRecordHandler { } } + public void init(List readSheetList, Boolean readAll) { + this.readSheetList = readSheetList; + this.readAll = readAll; + } + @Override public int getOrder() { return 0; diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java index 932fb650..dd468b4b 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java @@ -13,7 +13,9 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.usermodel.XSSFRelation; @@ -24,7 +26,7 @@ import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.exception.ExcelAnalysisException; @@ -32,12 +34,14 @@ import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.SheetUtils; +import com.alibaba.excel.util.StringUtils; /** * * @author jipengfei */ -public class XlsxSaxAnalyser implements ExcelExecutor { +public class XlsxSaxAnalyser implements ExcelReadExecutor { private AnalysisContext analysisContext; private List sheetList; @@ -135,7 +139,7 @@ public class XlsxSaxAnalyser implements ExcelExecutor { } else { FileUtils.writeToFile(tempFile, readWorkbookHolder.getInputStream()); } - return OPCPackage.open(tempFile); + return OPCPackage.open(tempFile, PackageAccess.READ); } @Override @@ -171,9 +175,16 @@ public class XlsxSaxAnalyser implements ExcelExecutor { } @Override - public void execute() { - parseXmlSource(sheetMap.get(analysisContext.readSheetHolder().getSheetNo()), - new XlsxRowHandler(analysisContext, stylesTable)); + public void execute(List readSheetList, Boolean readAll) { + for (ReadSheet readSheet : sheetList) { + readSheet = SheetUtils.match(readSheet, readSheetList, readAll, + analysisContext.readWorkbookHolder().getGlobalConfiguration()); + if (readSheet != null) { + analysisContext.currentSheet(readSheet); + parseXmlSource(sheetMap.get(readSheet.getSheetNo()), new XlsxRowHandler(analysisContext, stylesTable)); + // The last sheet is read + analysisContext.readSheetHolder().notifyAfterAllAnalysed(analysisContext); + } + } } - } diff --git a/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java b/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java new file mode 100644 index 00000000..1475ad3a --- /dev/null +++ b/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java @@ -0,0 +1,18 @@ +package com.alibaba.excel.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Ignore all unannotated fields. + * + * @author Jiaju Zhuang + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelIgnoreUnannotated { +} diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContext.java b/src/main/java/com/alibaba/excel/context/AnalysisContext.java index 7b8ba86c..f44c2b38 100644 --- a/src/main/java/com/alibaba/excel/context/AnalysisContext.java +++ b/src/main/java/com/alibaba/excel/context/AnalysisContext.java @@ -2,7 +2,7 @@ package com.alibaba.excel.context; import java.io.InputStream; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.read.metadata.ReadSheet; @@ -22,12 +22,10 @@ public interface AnalysisContext { /** * Select the current table * - * @param excelExecutor - * Excel file Executor * @param readSheet * sheet to read */ - void currentSheet(ExcelExecutor excelExecutor, ReadSheet readSheet); + void currentSheet(ReadSheet readSheet); /** * All information about the workbook you are currently working on diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java index d9d7ded5..e5ded50f 100644 --- a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java @@ -5,8 +5,6 @@ import java.io.InputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.analysis.ExcelExecutor; -import com.alibaba.excel.analysis.v07.XlsxSaxAnalyser; import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.read.metadata.ReadSheet; @@ -16,7 +14,6 @@ import com.alibaba.excel.read.metadata.holder.ReadRowHolder; import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; import com.alibaba.excel.support.ExcelTypeEnum; -import com.alibaba.excel.util.StringUtils; /** * @@ -53,13 +50,9 @@ public class AnalysisContextImpl implements AnalysisContext { } @Override - public void currentSheet(ExcelExecutor excelExecutor, ReadSheet readSheet) { - if (readSheet == null) { - throw new IllegalArgumentException("Sheet argument cannot be null."); - } + public void currentSheet(ReadSheet readSheet) { readSheetHolder = new ReadSheetHolder(readSheet, readWorkbookHolder); currentReadHolder = readSheetHolder; - selectSheet(excelExecutor); if (readWorkbookHolder.getHasReadSheet().contains(readSheetHolder.getSheetNo())) { throw new ExcelAnalysisException("Cannot read sheet repeatedly."); } @@ -69,54 +62,6 @@ public class AnalysisContextImpl implements AnalysisContext { } } - private void selectSheet(ExcelExecutor excelExecutor) { - if (excelExecutor instanceof XlsxSaxAnalyser) { - selectSheet07(excelExecutor); - } else { - selectSheet03(); - } - } - - private void selectSheet03() { - if (readSheetHolder.getSheetNo() != null && readSheetHolder.getSheetNo() >= 0) { - return; - } - if (!StringUtils.isEmpty(readSheetHolder.getSheetName())) { - LOGGER.warn("Excel 2003 does not support matching sheets by name, defaults to the first one."); - } - readSheetHolder.setSheetNo(0); - } - - private void selectSheet07(ExcelExecutor excelExecutor) { - if (readSheetHolder.getSheetNo() != null && readSheetHolder.getSheetNo() >= 0) { - for (ReadSheet readSheetExcel : excelExecutor.sheetList()) { - if (readSheetExcel.getSheetNo().equals(readSheetHolder.getSheetNo())) { - readSheetHolder.setSheetName(readSheetExcel.getSheetName()); - return; - } - } - throw new ExcelAnalysisException("Can not find sheet:" + readSheetHolder.getSheetNo()); - } - if (!StringUtils.isEmpty(readSheetHolder.getSheetName())) { - for (ReadSheet readSheetExcel : excelExecutor.sheetList()) { - String sheetName = readSheetExcel.getSheetName(); - if (sheetName == null) { - continue; - } - if (readSheetHolder.globalConfiguration().getAutoTrim()) { - sheetName = sheetName.trim(); - } - if (sheetName.equals(readSheetHolder.getSheetName())) { - readSheetHolder.setSheetNo(readSheetExcel.getSheetNo()); - return; - } - } - } - ReadSheet readSheetExcel = excelExecutor.sheetList().get(0); - readSheetHolder.setSheetNo(readSheetExcel.getSheetNo()); - readSheetHolder.setSheetName(readSheetExcel.getSheetName()); - } - @Override public ReadWorkbookHolder readWorkbookHolder() { return readWorkbookHolder; diff --git a/src/main/java/com/alibaba/excel/context/WriteContext.java b/src/main/java/com/alibaba/excel/context/WriteContext.java index 17db4ca2..c6e85c94 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContext.java +++ b/src/main/java/com/alibaba/excel/context/WriteContext.java @@ -5,6 +5,7 @@ import java.io.OutputStream; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import com.alibaba.excel.enums.WriteTypeEnum; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.holder.WriteHolder; @@ -21,9 +22,11 @@ public interface WriteContext { /** * If the current sheet already exists, select it; if not, create it * - * @param writeSheet Current sheet + * @param writeSheet + * Current sheet + * @param writeType */ - void currentSheet(WriteSheet writeSheet); + void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType); /** * If the current table already exists, select it; if not, create it @@ -66,6 +69,7 @@ public interface WriteContext { */ void finish(); + /** * Current sheet * diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index 6ad7fcbb..ffacd521 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -1,9 +1,28 @@ package com.alibaba.excel.context; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.StringUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; import java.io.OutputStream; -import java.util.List; +import java.security.GeneralSecurityException; import java.util.Map; - +import java.util.UUID; + +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.crypt.Decryptor; +import org.apache.poi.poifs.crypt.EncryptionInfo; +import org.apache.poi.poifs.crypt.EncryptionMode; +import org.apache.poi.poifs.crypt.Encryptor; +import org.apache.poi.poifs.filesystem.DocumentOutputStream; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; @@ -13,14 +32,12 @@ import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.excel.enums.WriteTypeEnum; import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.util.WorkBookUtil; -import com.alibaba.excel.write.handler.CellWriteHandler; -import com.alibaba.excel.write.handler.RowWriteHandler; -import com.alibaba.excel.write.handler.SheetWriteHandler; -import com.alibaba.excel.write.handler.WorkbookWriteHandler; -import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.util.WriteHandlerUtils; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteWorkbook; @@ -64,42 +81,18 @@ public class WriteContextImpl implements WriteContext { LOGGER.debug("Begin to Initialization 'WriteContextImpl'"); } initCurrentWorkbookHolder(writeWorkbook); - beforeWorkbookCreate(); + WriteHandlerUtils.beforeWorkbookCreate(this); try { - writeWorkbookHolder.setWorkbook(WorkBookUtil.createWorkBook(writeWorkbookHolder)); + WorkBookUtil.createWorkBook(writeWorkbookHolder); } catch (Exception e) { throw new ExcelGenerateException("Create workbook failure", e); } - afterWorkbookCreate(); + WriteHandlerUtils.afterWorkbookCreate(this); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Initialization 'WriteContextImpl' complete"); } } - private void beforeWorkbookCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(WorkbookWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof WorkbookWriteHandler) { - ((WorkbookWriteHandler)writeHandler).beforeWorkbookCreate(); - } - } - } - - private void afterWorkbookCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(WorkbookWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof WorkbookWriteHandler) { - ((WorkbookWriteHandler)writeHandler).afterWorkbookCreate(writeWorkbookHolder); - } - } - } - private void initCurrentWorkbookHolder(WriteWorkbook writeWorkbook) { writeWorkbookHolder = new WriteWorkbookHolder(writeWorkbook); currentWriteHolder = writeWorkbookHolder; @@ -112,7 +105,7 @@ public class WriteContextImpl implements WriteContext { * @param writeSheet */ @Override - public void currentSheet(WriteSheet writeSheet) { + public void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType) { if (writeSheet == null) { throw new IllegalArgumentException("Sheet argument cannot be null"); } @@ -136,38 +129,9 @@ public class WriteContextImpl implements WriteContext { return; } initCurrentSheetHolder(writeSheet); - beforeSheetCreate(); + WriteHandlerUtils.beforeSheetCreate(this); // Initialization current sheet - initSheet(); - afterSheetCreate(); - } - - private void beforeSheetCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(SheetWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof SheetWriteHandler) { - ((SheetWriteHandler)writeHandler).beforeSheetCreate(writeWorkbookHolder, writeSheetHolder); - } - } - } - - private void afterSheetCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(SheetWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof SheetWriteHandler) { - ((SheetWriteHandler)writeHandler).afterSheetCreate(writeWorkbookHolder, writeSheetHolder); - } - } - if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { - writeWorkbookHolder.getWriteWorkbook().getWriteHandler().sheet(writeSheetHolder.getSheetNo(), - writeSheetHolder.getSheet()); - } + initSheet(writeType); } private void initCurrentSheetHolder(WriteSheet writeSheet) { @@ -180,19 +144,25 @@ public class WriteContextImpl implements WriteContext { } } - private void initSheet() { + private void initSheet(WriteTypeEnum writeType) { Sheet currentSheet; try { currentSheet = writeWorkbookHolder.getWorkbook().getSheetAt(writeSheetHolder.getSheetNo()); + writeSheetHolder + .setCachedSheet(writeWorkbookHolder.getCachedWorkbook().getSheetAt(writeSheetHolder.getSheetNo())); } catch (Exception e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Can not find sheet:{} ,now create it", writeSheetHolder.getSheetNo()); } currentSheet = WorkBookUtil.createSheet(writeWorkbookHolder.getWorkbook(), writeSheetHolder.getSheetName()); + writeSheetHolder.setCachedSheet(currentSheet); } writeSheetHolder.setSheet(currentSheet); - // Initialization head - initHead(writeSheetHolder.excelWriteHeadProperty()); + WriteHandlerUtils.afterSheetCreate(this); + if (WriteTypeEnum.ADD.equals(writeType)) { + // Initialization head + initHead(writeSheetHolder.excelWriteHeadProperty()); + } } public void initHead(ExcelWriteHeadProperty excelWriteHeadProperty) { @@ -205,42 +175,13 @@ public class WriteContextImpl implements WriteContext { addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex); for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + newRowIndex; i++, relativeRowIndex++) { - beforeRowCreate(newRowIndex, relativeRowIndex); + WriteHandlerUtils.beforeRowCreate(this, newRowIndex, relativeRowIndex, Boolean.TRUE); Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i); - afterRowCreate(row, relativeRowIndex); + WriteHandlerUtils.afterRowCreate(this, row, relativeRowIndex, Boolean.TRUE); addOneRowOfHeadDataToExcel(row, excelWriteHeadProperty.getHeadMap(), relativeRowIndex); } } - private void beforeRowCreate(int rowIndex, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).beforeRowCreate(writeSheetHolder, writeTableHolder, rowIndex, - relativeRowIndex, true); - } - } - } - - private void afterRowCreate(Row row, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).afterRowCreate(writeSheetHolder, writeTableHolder, row, - relativeRowIndex, true); - } - } - if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { - writeWorkbookHolder.getWriteWorkbook().getWriteHandler().row(row.getRowNum(), row); - } - } - private void addMergedRegionToCurrentSheet(ExcelWriteHeadProperty excelWriteHeadProperty, int rowIndex) { for (com.alibaba.excel.metadata.CellRange cellRangeModel : excelWriteHeadProperty.headCellRangeList()) { writeSheetHolder.getSheet().addMergedRegion(new CellRangeAddress(cellRangeModel.getFirstRow() + rowIndex, @@ -251,38 +192,13 @@ public class WriteContextImpl implements WriteContext { private void addOneRowOfHeadDataToExcel(Row row, Map headMap, int relativeRowIndex) { for (Map.Entry entry : headMap.entrySet()) { Head head = entry.getValue(); - beforeCellCreate(row, head, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, entry.getKey(), head.getHeadNameList().get(relativeRowIndex)); - afterCellCreate(head, cell, relativeRowIndex); - } - } - - private void beforeCellCreate(Row row, Head head, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).beforeCellCreate(writeSheetHolder, writeTableHolder, row, head, - relativeRowIndex, true); - } - } - } - - private void afterCellCreate(Head head, Cell cell, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).afterCellCreate(writeSheetHolder, writeTableHolder, null, cell, head, - relativeRowIndex, true); - } - } - if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { - writeWorkbookHolder.getWriteWorkbook().getWriteHandler().cell(cell.getRowIndex(), cell); + int columnIndex = entry.getKey(); + WriteHandlerUtils.beforeCellCreate(this, row, head, columnIndex, relativeRowIndex, Boolean.TRUE); + Cell cell = row.createCell(columnIndex); + WriteHandlerUtils.afterCellCreate(this, cell, head, relativeRowIndex, Boolean.TRUE); + cell.setCellValue(head.getHeadNameList().get(relativeRowIndex)); + CellData cellData = null; + WriteHandlerUtils.afterCellDispose(this, cellData, cell, head, relativeRowIndex, Boolean.TRUE); } } @@ -341,16 +257,27 @@ public class WriteContextImpl implements WriteContext { @Override public void finish() { + WriteHandlerUtils.afterWorkbookDispose(this); if (writeWorkbookHolder == null) { return; } + boolean isOutputStreamEncrypt = false; try { - writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream()); - writeWorkbookHolder.getWorkbook().close(); + isOutputStreamEncrypt = doOutputStreamEncrypt07(); } catch (Throwable t) { throwCanNotCloseIo(t); } + + if (!isOutputStreamEncrypt) { + try { + writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream()); + writeWorkbookHolder.getWorkbook().close(); + } catch (Throwable t) { + throwCanNotCloseIo(t); + } + } + try { Workbook workbook = writeWorkbookHolder.getWorkbook(); if (workbook instanceof SXSSFWorkbook) { @@ -359,6 +286,7 @@ public class WriteContextImpl implements WriteContext { } catch (Throwable t) { throwCanNotCloseIo(t); } + try { if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getOutputStream() != null) { writeWorkbookHolder.getOutputStream().close(); @@ -366,21 +294,25 @@ public class WriteContextImpl implements WriteContext { } catch (Throwable t) { throwCanNotCloseIo(t); } - try { - if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getTemplateInputStream() != null) { - writeWorkbookHolder.getTemplateInputStream().close(); + + if (!isOutputStreamEncrypt) { + try { + doFileEncrypt07(); + } catch (Throwable t) { + throwCanNotCloseIo(t); } - } catch (Throwable t) { - throwCanNotCloseIo(t); } + try { - if (!writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getFile() != null - && writeWorkbookHolder.getOutputStream() != null) { - writeWorkbookHolder.getOutputStream().close(); + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + writeWorkbookHolder.getTempTemplateInputStream().close(); } } catch (Throwable t) { throwCanNotCloseIo(t); } + + clearEncrypt03(); + if (LOGGER.isDebugEnabled()) { LOGGER.debug("Finished write."); } @@ -409,4 +341,95 @@ public class WriteContextImpl implements WriteContext { public Workbook getWorkbook() { return writeWorkbookHolder.getWorkbook(); } + + private void clearEncrypt03() { + if (StringUtils.isEmpty(writeWorkbookHolder.getPassword()) + || !ExcelTypeEnum.XLS.equals(writeWorkbookHolder.getExcelType())) { + return; + } + Biff8EncryptionKey.setCurrentUserPassword(null); + } + + /** + * To encrypt + */ + private boolean doOutputStreamEncrypt07() throws Exception { + if (StringUtils.isEmpty(writeWorkbookHolder.getPassword()) + || !ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) { + return false; + } + if (writeWorkbookHolder.getFile() != null) { + return false; + } + File tempXlsx = FileUtils.createTmpFile(UUID.randomUUID().toString() + ".xlsx"); + FileOutputStream tempFileOutputStream = new FileOutputStream(tempXlsx); + try { + writeWorkbookHolder.getWorkbook().write(tempFileOutputStream); + } finally { + try { + writeWorkbookHolder.getWorkbook().close(); + tempFileOutputStream.close(); + } catch (Exception e) { + if (!tempXlsx.delete()) { + throw new ExcelGenerateException("Can not delete temp File!"); + } + throw e; + } + } + POIFSFileSystem fileSystem = null; + try { + fileSystem = openFileSystemAndEncrypt(tempXlsx); + fileSystem.writeFilesystem(writeWorkbookHolder.getOutputStream()); + } finally { + if (fileSystem != null) { + fileSystem.close(); + } + if (!tempXlsx.delete()) { + throw new ExcelGenerateException("Can not delete temp File!"); + } + } + return true; + } + + /** + * To encrypt + */ + private void doFileEncrypt07() throws Exception { + if (StringUtils.isEmpty(writeWorkbookHolder.getPassword()) + || !ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) { + return; + } + if (writeWorkbookHolder.getFile() == null) { + return; + } + FileOutputStream fileOutputStream = null; + POIFSFileSystem fileSystem = null; + try { + fileSystem = openFileSystemAndEncrypt(writeWorkbookHolder.getFile()); + fileOutputStream = new FileOutputStream(writeWorkbookHolder.getFile()); + fileSystem.writeFilesystem(fileOutputStream); + } finally { + if (fileOutputStream != null) { + fileOutputStream.close(); + } + if (fileSystem != null) { + fileSystem.close(); + } + } + } + + private POIFSFileSystem openFileSystemAndEncrypt(File file) throws Exception { + POIFSFileSystem fileSystem = new POIFSFileSystem(); + Encryptor encryptor = new EncryptionInfo(EncryptionMode.standard).getEncryptor(); + encryptor.confirmPassword(writeWorkbookHolder.getPassword()); + OPCPackage opcPackage = null; + try { + opcPackage = OPCPackage.open(file, PackageAccess.READ_WRITE); + OutputStream outputStream = encryptor.getDataStream(fileSystem); + opcPackage.save(outputStream); + } finally { + opcPackage.close(); + } + return fileSystem; + } } diff --git a/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java b/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java new file mode 100644 index 00000000..78803f18 --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Direction of writing + * + * @author Jiaju Zhuang + **/ +public enum WriteDirectionEnum { + /** + * Vertical write. + */ + VERTICAL, + /** + * Horizontal write. + */ + HORIZONTAL,; +} diff --git a/src/main/java/com/alibaba/excel/enums/WriteLastRowType.java b/src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java similarity index 91% rename from src/main/java/com/alibaba/excel/enums/WriteLastRowType.java rename to src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java index bd477bdd..d9a41aed 100644 --- a/src/main/java/com/alibaba/excel/enums/WriteLastRowType.java +++ b/src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java @@ -5,7 +5,7 @@ package com.alibaba.excel.enums; * * @author Jiaju Zhuang **/ -public enum WriteLastRowType { +public enum WriteLastRowTypeEnum { /** * Excel are created without templates ,And any data has been written; */ diff --git a/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java b/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java new file mode 100644 index 00000000..e9d22a8b --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Type of template to read when writing + * + * @author Jiaju Zhuang + **/ +public enum WriteTemplateAnalysisCellTypeEnum { + /** + * Common field. + */ + COMMON, + /** + * A collection of fields. + */ + COLLECTION,; +} diff --git a/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java b/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java new file mode 100644 index 00000000..9dbbd314 --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Enumeration of write methods + * + * @author Jiaju Zhuang + **/ +public enum WriteTypeEnum { + /** + * Add. + */ + ADD, + /** + * Fill. + */ + FILL,; +} diff --git a/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java b/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java index 74630f04..4af25690 100644 --- a/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java +++ b/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java @@ -1,16 +1,49 @@ package com.alibaba.excel.exception; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.write.builder.ExcelWriterBuilder; + /** * Data convert exception * * @author Jiaju Zhuang */ public class ExcelDataConvertException extends RuntimeException { + /** + * NotNull. + */ + private Integer rowIndex; + /** + * NotNull. + */ + private Integer columnIndex; + /** + * Nullable.Only when the header is configured and when the class header is used is not null. + * + * @see {@link ExcelWriterBuilder#head(Class)} + */ + private ExcelContentProperty excelContentProperty; public ExcelDataConvertException(String message) { super(message); } + public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty, + String message) { + super(message); + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + this.excelContentProperty = excelContentProperty; + } + + public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty, + String message, Throwable cause) { + super(message, cause); + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + this.excelContentProperty = excelContentProperty; + } + public ExcelDataConvertException(String message, Throwable cause) { super(message, cause); } @@ -18,4 +51,28 @@ public class ExcelDataConvertException extends RuntimeException { public ExcelDataConvertException(Throwable cause) { super(cause); } + + public Integer getRowIndex() { + return rowIndex; + } + + public void setRowIndex(Integer rowIndex) { + this.rowIndex = rowIndex; + } + + public Integer getColumnIndex() { + return columnIndex; + } + + public void setColumnIndex(Integer columnIndex) { + this.columnIndex = columnIndex; + } + + public ExcelContentProperty getExcelContentProperty() { + return excelContentProperty; + } + + public void setExcelContentProperty(ExcelContentProperty excelContentProperty) { + this.excelContentProperty = excelContentProperty; + } } diff --git a/src/main/java/com/alibaba/excel/metadata/BasicParameter.java b/src/main/java/com/alibaba/excel/metadata/BasicParameter.java index 89d78511..40bffe63 100644 --- a/src/main/java/com/alibaba/excel/metadata/BasicParameter.java +++ b/src/main/java/com/alibaba/excel/metadata/BasicParameter.java @@ -74,4 +74,5 @@ public class BasicParameter { public void setUse1904windowing(Boolean use1904windowing) { this.use1904windowing = use1904windowing; } + } diff --git a/src/main/java/com/alibaba/excel/metadata/CellData.java b/src/main/java/com/alibaba/excel/metadata/CellData.java index 7eb8de65..a3a40c61 100644 --- a/src/main/java/com/alibaba/excel/metadata/CellData.java +++ b/src/main/java/com/alibaba/excel/metadata/CellData.java @@ -6,11 +6,13 @@ import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.util.StringUtils; /** - * Excel internal cell data + * Excel internal cell data. + * + *

* * @author Jiaju Zhuang */ -public class CellData { +public class CellData { private CellDataTypeEnum type; /** * {@link CellDataTypeEnum#NUMBER} @@ -35,8 +37,12 @@ public class CellData { * The string of number formatting.Currently only supported when reading */ private String dataFormatString; + /** + * The resulting converted data. + */ + private T data; - public CellData(CellData other) { + public CellData(CellData other) { this.type = other.type; this.numberValue = other.numberValue; this.stringValue = other.stringValue; @@ -46,6 +52,19 @@ public class CellData { this.imageValue = other.imageValue; this.dataFormat = other.dataFormat; this.dataFormatString = other.dataFormatString; + this.data = other.data; + } + + public CellData() {} + + public CellData(T data) { + this.data = data; + } + + public CellData(T data, String formulaValue) { + this.data = data; + this.formula = Boolean.TRUE; + this.formulaValue = formulaValue; } public CellData(String stringValue) { @@ -171,6 +190,14 @@ public class CellData { this.dataFormatString = dataFormatString; } + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + /** * Ensure that the object does not appear null */ diff --git a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java index 096d7d6c..69211617 100644 --- a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -13,9 +13,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.converters.AutoConverter; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.HeadKindEnum; @@ -53,6 +55,10 @@ public class ExcelHeadProperty { * Configuration column information */ private Map contentPropertyMap; + /** + * Configuration column information + */ + private Map fieldNameContentPropertyMap; /** * Fields ignored */ @@ -62,6 +68,7 @@ public class ExcelHeadProperty { this.headClazz = headClazz; headMap = new TreeMap(); contentPropertyMap = new TreeMap(); + fieldNameContentPropertyMap = new HashMap(); ignoreMap = new HashMap(16); headKind = HeadKindEnum.NONE; headRowNumber = 0; @@ -79,10 +86,6 @@ public class ExcelHeadProperty { if (LOGGER.isDebugEnabled()) { LOGGER.debug("The initialization sheet/table 'ExcelHeadProperty' is complete , head kind is {}", headKind); } - if (!hasHead()) { - LOGGER.warn( - "The table has no header set and all annotations will not be read.If you want to use annotations, please use set head class in ExcelWriterBuilder/ExcelWriterSheetBuilder/ExcelWriterTableBuilder"); - } } private void initHeadRowNumber() { @@ -119,25 +122,28 @@ public class ExcelHeadProperty { tempClass = tempClass.getSuperclass(); } + ExcelIgnoreUnannotated excelIgnoreUnannotated = + (ExcelIgnoreUnannotated)headClazz.getAnnotation(ExcelIgnoreUnannotated.class); // Screening of field List defaultFieldList = new ArrayList(); Map customFiledMap = new TreeMap(); for (Field field : fieldList) { ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class); - if(Modifier.isStatic(field.getModifiers())&&Modifier.isFinal(field.getModifiers())){ - ignoreMap.put(field.getName(),field); - continue; - } - if(Modifier.isTransient(field.getModifiers())){ - ignoreMap.put(field.getName(),field); - continue; - } if (excelIgnore != null) { ignoreMap.put(field.getName(), field); continue; } ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); - if (excelProperty == null && convertAllFiled != null && !convertAllFiled) { + boolean noExcelProperty = excelProperty == null + && ((convertAllFiled != null && !convertAllFiled) || excelIgnoreUnannotated != null); + if (noExcelProperty) { + ignoreMap.put(field.getName(), field); + continue; + } + boolean isStaticFinalOrTransient = + (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) + || Modifier.isTransient(field.getModifiers()); + if (excelProperty == null && isStaticFinalOrTransient) { ignoreMap.put(field.getName(), field); continue; } @@ -199,6 +205,7 @@ public class ExcelHeadProperty { .setNumberFormatProperty(NumberFormatProperty.build(field.getAnnotation(NumberFormat.class))); headMap.put(index, head); contentPropertyMap.put(index, excelContentProperty); + fieldNameContentPropertyMap.put(field.getName(), excelContentProperty); } public Class getHeadClazz() { @@ -245,6 +252,14 @@ public class ExcelHeadProperty { this.contentPropertyMap = contentPropertyMap; } + public Map getFieldNameContentPropertyMap() { + return fieldNameContentPropertyMap; + } + + public void setFieldNameContentPropertyMap(Map fieldNameContentPropertyMap) { + this.fieldNameContentPropertyMap = fieldNameContentPropertyMap; + } + public Map getIgnoreMap() { return ignoreMap; } diff --git a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java index 2b5bcd6e..340548b9 100644 --- a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java +++ b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java @@ -222,10 +222,28 @@ public class ExcelReaderBuilder { return this; } + /** + * Whether the encryption + * + * @param password + * @return + */ + public ExcelReaderBuilder password(String password) { + readWorkbook.setPassword(password); + return this; + } + public ExcelReader build() { return new ExcelReader(readWorkbook); } + public ExcelReader doReadAll() { + ExcelReader excelReader = build(); + excelReader.readAll(); + excelReader.finish(); + return excelReader; + } + public ExcelReaderSheetBuilder sheet() { return sheet(null, null); } diff --git a/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java b/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java index 7896334f..a368024f 100644 --- a/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java +++ b/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java @@ -33,7 +33,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener public void invoke(Map cellDataMap, AnalysisContext context) { ReadHolder currentReadHolder = context.currentReadHolder(); if (HeadKindEnum.CLASS.equals(currentReadHolder.excelReadHeadProperty().getHeadKind())) { - context.readRowHolder().setCurrentRowAnalysisResult(buildUserModel(cellDataMap, currentReadHolder)); + context.readRowHolder() + .setCurrentRowAnalysisResult(buildUserModel(cellDataMap, currentReadHolder, context)); return; } context.readRowHolder().setCurrentRowAnalysisResult(buildStringList(cellDataMap, currentReadHolder, context)); @@ -41,35 +42,51 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener private Object buildStringList(Map cellDataMap, ReadHolder currentReadHolder, AnalysisContext context) { + int index = 0; if (context.readWorkbookHolder().getDefaultReturnMap()) { Map map = new HashMap(cellDataMap.size() * 4 / 3 + 1); for (Map.Entry entry : cellDataMap.entrySet()) { + Integer key = entry.getKey(); CellData cellData = entry.getValue(); + while (index < key) { + map.put(index, null); + index++; + } + index++; if (cellData.getType() == CellDataTypeEnum.EMPTY) { - map.put(entry.getKey(), null); + map.put(key, null); continue; } - map.put(entry.getKey(), (String)ConverterUtils.convertToJavaObject(cellData, String.class, null, - currentReadHolder.converterMap(), currentReadHolder.globalConfiguration())); + map.put(key, + (String)ConverterUtils.convertToJavaObject(cellData, null, null, currentReadHolder.converterMap(), + currentReadHolder.globalConfiguration(), context.readRowHolder().getRowIndex(), key)); } return map; } else { // Compatible with the old code the old code returns a list List list = new ArrayList(); for (Map.Entry entry : cellDataMap.entrySet()) { + Integer key = entry.getKey(); CellData cellData = entry.getValue(); + while (index < key) { + list.add(null); + index++; + } + index++; if (cellData.getType() == CellDataTypeEnum.EMPTY) { list.add(null); continue; } - list.add((String)ConverterUtils.convertToJavaObject(cellData, String.class, null, - currentReadHolder.converterMap(), currentReadHolder.globalConfiguration())); + list.add( + (String)ConverterUtils.convertToJavaObject(cellData, null, null, currentReadHolder.converterMap(), + currentReadHolder.globalConfiguration(), context.readRowHolder().getRowIndex(), key)); } return list; } } - private Object buildUserModel(Map cellDataMap, ReadHolder currentReadHolder) { + private Object buildUserModel(Map cellDataMap, ReadHolder currentReadHolder, + AnalysisContext context) { ExcelReadHeadProperty excelReadHeadProperty = currentReadHolder.excelReadHeadProperty(); Object resultModel; try { @@ -91,8 +108,9 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener continue; } ExcelContentProperty excelContentProperty = contentPropertyMap.get(index); - Object value = ConverterUtils.convertToJavaObject(cellData, excelContentProperty.getField().getType(), - excelContentProperty, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration()); + Object value = ConverterUtils.convertToJavaObject(cellData, excelContentProperty.getField(), + excelContentProperty, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration(), + context.readRowHolder().getRowIndex(), index); if (value != null) { map.put(excelContentProperty.getField().getName(), value); } diff --git a/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java b/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java index f3a3a7b3..5b6f8dec 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java +++ b/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java @@ -42,6 +42,19 @@ public class ReadSheet extends ReadBasicParameter { this.sheetName = sheetName; } + public void copyBasicParameter(ReadSheet other) { + if (other == null) { + return; + } + this.setHeadRowNumber(other.getHeadRowNumber()); + this.setCustomReadListenerList(other.getCustomReadListenerList()); + this.setHead(other.getHead()); + this.setClazz(other.getClazz()); + this.setCustomConverterList(other.getCustomConverterList()); + this.setAutoTrim(other.getAutoTrim()); + this.setUse1904windowing(other.getUse1904windowing()); + } + @Override public String toString() { return "ReadSheet{" + "sheetNo=" + sheetNo + ", sheetName='" + sheetName + '\'' + "} " + super.toString(); diff --git a/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java b/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java index b21cdb80..3a873da5 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java +++ b/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java @@ -59,6 +59,10 @@ public class ReadWorkbook extends ReadBasicParameter { * Select the cache.Default use {@link com.alibaba.excel.cache.selector.SimpleReadCacheSelector} */ private ReadCacheSelector readCacheSelector; + /** + * Whether the encryption + */ + private String password; /** * The default is all excel objects.Default is true. *

@@ -164,4 +168,12 @@ public class ReadWorkbook extends ReadBasicParameter { public void setReadCacheSelector(ReadCacheSelector readCacheSelector) { this.readCacheSelector = readCacheSelector; } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } } 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 3fd88395..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 @@ -8,8 +8,6 @@ import java.util.Set; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.poifs.filesystem.POIFSFileSystem; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.selector.EternalReadCacheSelector; @@ -28,7 +26,6 @@ import com.alibaba.excel.support.ExcelTypeEnum; * @author Jiaju Zhuang */ public class ReadWorkbookHolder extends AbstractReadHolder { - private static final Logger LOGGER = LoggerFactory.getLogger(ReadWorkbookHolder.class); /** * current param @@ -82,7 +79,10 @@ public class ReadWorkbookHolder extends AbstractReadHolder { * Temporary files when reading excel */ private File tempFile; - + /** + * Whether the encryption + */ + private String password; /** * The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a * field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed. @@ -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); } @@ -174,6 +174,7 @@ public class ReadWorkbookHolder extends AbstractReadHolder { } this.hasReadSheet = new HashSet(); this.ignoreRecord03 = Boolean.FALSE; + this.password = readWorkbook.getPassword(); } public ReadWorkbook getReadWorkbook() { @@ -312,6 +313,14 @@ public class ReadWorkbookHolder extends AbstractReadHolder { this.ignoreRecord03 = ignoreRecord03; } + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + @Override public HolderEnum holderType() { return HolderEnum.WORKBOOK; 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/util/ConverterUtils.java b/src/main/java/com/alibaba/excel/util/ConverterUtils.java index 7445b4ce..b6244c23 100644 --- a/src/main/java/com/alibaba/excel/util/ConverterUtils.java +++ b/src/main/java/com/alibaba/excel/util/ConverterUtils.java @@ -1,5 +1,8 @@ package com.alibaba.excel.util; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; @@ -56,17 +59,55 @@ public class ConverterUtils { * Convert it into a Java object * * @param cellData - * @param clazz + * @param field * @param contentProperty * @param converterMap * @param globalConfiguration + * @param rowIndex + * @param columnIndex * @return */ - public static Object convertToJavaObject(CellData cellData, Class clazz, ExcelContentProperty contentProperty, - Map converterMap, GlobalConfiguration globalConfiguration) { + public static Object convertToJavaObject(CellData cellData, Field field, ExcelContentProperty contentProperty, + Map converterMap, GlobalConfiguration globalConfiguration, Integer rowIndex, + Integer columnIndex) { + Class clazz; + if (field == null) { + clazz = String.class; + } else { + clazz = field.getType(); + } if (clazz == CellData.class) { - return new CellData(cellData); + Type type = field.getGenericType(); + Class classGeneric; + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType)type; + classGeneric = (Class)parameterizedType.getActualTypeArguments()[0]; + } else { + classGeneric = String.class; + } + CellData cellDataReturn = new CellData(cellData); + cellDataReturn.setData(doConvertToJavaObject(cellData, classGeneric, contentProperty, converterMap, + globalConfiguration, rowIndex, columnIndex)); + return cellDataReturn; } + return doConvertToJavaObject(cellData, clazz, contentProperty, converterMap, globalConfiguration, rowIndex, + columnIndex); + } + + /** + * + * @param cellData + * @param clazz + * @param contentProperty + * @param converterMap + * @param globalConfiguration + * @param rowIndex + * @param columnIndex + * @return + */ + private static Object doConvertToJavaObject(CellData cellData, Class clazz, ExcelContentProperty contentProperty, + Map converterMap, GlobalConfiguration globalConfiguration, Integer rowIndex, + Integer columnIndex) { Converter converter = null; if (contentProperty != null) { converter = contentProperty.getConverter(); @@ -75,13 +116,14 @@ public class ConverterUtils { converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType())); } if (converter == null) { - throw new ExcelDataConvertException( + throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty, "Converter not found, convert " + cellData.getType() + " to " + clazz.getName()); } try { return converter.convertToJavaData(cellData, contentProperty, globalConfiguration); } catch (Exception e) { - throw new ExcelDataConvertException("Convert data " + cellData + " to " + clazz + " error ", e); + throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty, + "Convert data " + cellData + " to " + clazz + " error ", e); } } } diff --git a/src/main/java/com/alibaba/excel/util/FileUtils.java b/src/main/java/com/alibaba/excel/util/FileUtils.java index 9738b50d..34e68530 100644 --- a/src/main/java/com/alibaba/excel/util/FileUtils.java +++ b/src/main/java/com/alibaba/excel/util/FileUtils.java @@ -157,6 +157,15 @@ public class FileUtils { return directory; } + public static File createTmpFile(String fileName) { + String tmpDir = System.getProperty(JAVA_IO_TMPDIR); + if (tmpDir == null) { + throw new RuntimeException( + "Systems temporary directory not defined - set the -D" + JAVA_IO_TMPDIR + " jvm property!"); + } + return new File(tmpDir, fileName); + } + /** * * @param directory diff --git a/src/main/java/com/alibaba/excel/util/SheetUtils.java b/src/main/java/com/alibaba/excel/util/SheetUtils.java new file mode 100644 index 00000000..66a1c480 --- /dev/null +++ b/src/main/java/com/alibaba/excel/util/SheetUtils.java @@ -0,0 +1,67 @@ +package com.alibaba.excel.util; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.read.metadata.ReadSheet; + +/** + * Sheet utils + * + * @author Jiaju Zhuang + */ +public class SheetUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(SheetUtils.class); + + private SheetUtils() {} + + /** + * Match the parameters to the actual sheet + * + * @param readSheet + * actual sheet + * @param parameterReadSheetList + * parameters + * @param readAll + * @return + */ + public static ReadSheet match(ReadSheet readSheet, List parameterReadSheetList, Boolean readAll, + GlobalConfiguration globalConfiguration) { + if (readAll) { + return readSheet; + } + for (ReadSheet parameterReadSheet : parameterReadSheetList) { + if (parameterReadSheet == null) { + continue; + } + if (parameterReadSheet.getSheetNo() == null && parameterReadSheet.getSheetName() == null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("The first is read by default."); + } + parameterReadSheet.setSheetNo(0); + } + boolean match = (parameterReadSheet.getSheetNo() != null + && parameterReadSheet.getSheetNo().equals(readSheet.getSheetNo())); + if (!match) { + String parameterSheetName = parameterReadSheet.getSheetName(); + if (!StringUtils.isEmpty(parameterSheetName)) { + boolean autoTrim = (parameterReadSheet.getAutoTrim() != null && parameterReadSheet.getAutoTrim()) + || (parameterReadSheet.getAutoTrim() == null && globalConfiguration.getAutoTrim()); + if (autoTrim) { + parameterSheetName = parameterSheetName.trim(); + } + match = parameterSheetName.equals(readSheet.getSheetName()); + } + } + if (match) { + readSheet.copyBasicParameter(parameterReadSheet); + return readSheet; + } + } + return null; + } + +} diff --git a/src/main/java/com/alibaba/excel/util/StringUtils.java b/src/main/java/com/alibaba/excel/util/StringUtils.java index 25ed127d..948ae652 100644 --- a/src/main/java/com/alibaba/excel/util/StringUtils.java +++ b/src/main/java/com/alibaba/excel/util/StringUtils.java @@ -6,10 +6,11 @@ package com.alibaba.excel.util; * @author jipengfei */ public class StringUtils { + public static final String EMPTY = ""; private StringUtils() {} public static boolean isEmpty(Object str) { - return (str == null || "".equals(str)); + return (str == null || EMPTY.equals(str)); } } diff --git a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java index b9579b17..bcec5234 100644 --- a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java +++ b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java @@ -2,8 +2,8 @@ package com.alibaba.excel.util; import java.io.IOException; +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; @@ -22,35 +22,35 @@ import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; */ public class WorkBookUtil { + private static final int ROW_ACCESS_WINDOW_SIZE = 500; + private WorkBookUtil() {} - public static Workbook createWorkBook(WriteWorkbookHolder writeWorkbookHolder) - throws IOException, InvalidFormatException { + public static void createWorkBook(WriteWorkbookHolder writeWorkbookHolder) throws IOException { if (ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) { - XSSFWorkbook xssfWorkbook = null; - if (writeWorkbookHolder.getTemplateFile() != null) { - xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTemplateFile()); - } - if (writeWorkbookHolder.getTemplateInputStream() != null) { - xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTemplateInputStream()); - } - // When using SXSSFWorkbook, you can't get the actual last line.But we need to read the last line when we - // are using the template, so we cache it - if (xssfWorkbook != null) { - for (int i = 0; i < xssfWorkbook.getNumberOfSheets(); i++) { - writeWorkbookHolder.getTemplateLastRowMap().put(i, xssfWorkbook.getSheetAt(i).getLastRowNum()); - } - return new SXSSFWorkbook(xssfWorkbook); + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTempTemplateInputStream()); + writeWorkbookHolder.setCachedWorkbook(xssfWorkbook); + writeWorkbookHolder.setWorkbook(new SXSSFWorkbook(xssfWorkbook, ROW_ACCESS_WINDOW_SIZE)); + return; } - return new SXSSFWorkbook(500); + SXSSFWorkbook sxssWorkbook = new SXSSFWorkbook(ROW_ACCESS_WINDOW_SIZE); + writeWorkbookHolder.setCachedWorkbook(sxssWorkbook); + writeWorkbookHolder.setWorkbook(sxssWorkbook); + return; } - if (writeWorkbookHolder.getTemplateFile() != null) { - return new HSSFWorkbook(new POIFSFileSystem(writeWorkbookHolder.getTemplateFile())); + HSSFWorkbook hssfWorkbook; + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + hssfWorkbook = new HSSFWorkbook(new POIFSFileSystem(writeWorkbookHolder.getTempTemplateInputStream())); + } else { + hssfWorkbook = new HSSFWorkbook(); } - if (writeWorkbookHolder.getTemplateInputStream() != null) { - return new HSSFWorkbook(new POIFSFileSystem(writeWorkbookHolder.getTemplateInputStream())); + writeWorkbookHolder.setCachedWorkbook(hssfWorkbook); + writeWorkbookHolder.setWorkbook(hssfWorkbook); + if (writeWorkbookHolder.getPassword() != null) { + Biff8EncryptionKey.setCurrentUserPassword(writeWorkbookHolder.getPassword()); + hssfWorkbook.writeProtectWorkbook(writeWorkbookHolder.getPassword(), StringUtils.EMPTY); } - return new HSSFWorkbook(); } public static Sheet createSheet(Workbook workbook, String sheetName) { diff --git a/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java b/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java new file mode 100644 index 00000000..b303a31e --- /dev/null +++ b/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java @@ -0,0 +1,196 @@ +package com.alibaba.excel.util; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.handler.WorkbookWriteHandler; +import com.alibaba.excel.write.handler.WriteHandler; + +/** + * Write handler utils + * + * @author Jiaju Zhuang + */ +public class WriteHandlerUtils { + + private WriteHandlerUtils() {} + + public static void beforeWorkbookCreate(WriteContext writeContext) { + List handlerList = + writeContext.writeWorkbookHolder().writeHandlerMap().get(WorkbookWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof WorkbookWriteHandler) { + ((WorkbookWriteHandler)writeHandler).beforeWorkbookCreate(); + } + } + } + + public static void afterWorkbookCreate(WriteContext writeContext) { + List handlerList = + writeContext.writeWorkbookHolder().writeHandlerMap().get(WorkbookWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof WorkbookWriteHandler) { + ((WorkbookWriteHandler)writeHandler).afterWorkbookCreate(writeContext.writeWorkbookHolder()); + } + } + } + + public static void afterWorkbookDispose(WriteContext writeContext) { + List handlerList = + writeContext.writeWorkbookHolder().writeHandlerMap().get(WorkbookWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof WorkbookWriteHandler) { + ((WorkbookWriteHandler)writeHandler).afterWorkbookDispose(writeContext.writeWorkbookHolder()); + } + } + } + + public static void beforeSheetCreate(WriteContext writeContext) { + List handlerList = writeContext.writeSheetHolder().writeHandlerMap().get(SheetWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof SheetWriteHandler) { + ((SheetWriteHandler)writeHandler).beforeSheetCreate(writeContext.writeWorkbookHolder(), + writeContext.writeSheetHolder()); + } + } + } + + public static void afterSheetCreate(WriteContext writeContext) { + List handlerList = writeContext.writeSheetHolder().writeHandlerMap().get(SheetWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof SheetWriteHandler) { + ((SheetWriteHandler)writeHandler).afterSheetCreate(writeContext.writeWorkbookHolder(), + writeContext.writeSheetHolder()); + } + } + if (null != writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { + writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler() + .sheet(writeContext.writeSheetHolder().getSheetNo(), writeContext.writeSheetHolder().getSheet()); + } + } + + public static void beforeCellCreate(WriteContext writeContext, Row row, Head head, Integer columnIndex, + Integer relativeRowIndex, Boolean isHead) { + List handlerList = + writeContext.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof CellWriteHandler) { + ((CellWriteHandler)writeHandler).beforeCellCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), row, head, columnIndex, relativeRowIndex, isHead); + } + } + } + + public static void afterCellCreate(WriteContext writeContext, Cell cell, Head head, Integer relativeRowIndex, + Boolean isHead) { + List handlerList = + writeContext.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof CellWriteHandler) { + ((CellWriteHandler)writeHandler).afterCellCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), cell, head, relativeRowIndex, isHead); + } + } + } + + public static void afterCellDispose(WriteContext writeContext, CellData cellData, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + List cellDataList = new ArrayList(); + if (cell != null) { + cellDataList.add(cellData); + } + afterCellDispose(writeContext, cellDataList, cell, head, relativeRowIndex, isHead); + } + + public static void afterCellDispose(WriteContext writeContext, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + List handlerList = + writeContext.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof CellWriteHandler) { + ((CellWriteHandler)writeHandler).afterCellDispose(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), cellDataList, cell, head, relativeRowIndex, isHead); + } + } + if (null != writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { + writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().cell(cell.getRowIndex(), cell); + } + } + + public static void beforeRowCreate(WriteContext writeContext, Integer rowIndex, Integer relativeRowIndex, + Boolean isHead) { + List handlerList = writeContext.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof RowWriteHandler) { + ((RowWriteHandler)writeHandler).beforeRowCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), rowIndex, relativeRowIndex, isHead); + } + } + } + + public static void afterRowCreate(WriteContext writeContext, Row row, Integer relativeRowIndex, Boolean isHead) { + List handlerList = writeContext.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof RowWriteHandler) { + ((RowWriteHandler)writeHandler).afterRowCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), row, relativeRowIndex, isHead); + } + } + + } + + public static void afterRowDispose(WriteContext writeContext, Row row, Integer relativeRowIndex, Boolean isHead) { + List handlerList = writeContext.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof RowWriteHandler) { + ((RowWriteHandler)writeHandler).afterRowDispose(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), row, relativeRowIndex, isHead); + } + } + if (null != writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { + writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().row(row.getRowNum(), row); + } + } +} diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java index a801cc4c..8a7d57da 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java @@ -6,6 +6,7 @@ import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * @author jipengfei @@ -36,6 +37,15 @@ public interface ExcelBuilder { */ void addContent(List data, WriteSheet writeSheet, WriteTable writeTable); + /** + * WorkBook fill value + * + * @param data + * @param fillConfig + * @param writeSheet + */ + void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet); + /** * Creates new cell range. Indexes are zero-based. * @@ -63,4 +73,13 @@ public interface ExcelBuilder { * Close io */ void finish(); + + /** + * add password + * @param data + * @param writeSheet + * @param writeTable + * @param password + */ + void addContent(List data, WriteSheet writeSheet, WriteTable writeTable, String password); } diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index 3ac3c5b4..fc017fdd 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -1,46 +1,20 @@ package com.alibaba.excel.write; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.CreationHelper; -import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.context.WriteContextImpl; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.converters.ConverterKeyBuild; -import com.alibaba.excel.enums.HeadKindEnum; -import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.enums.WriteTypeEnum; import com.alibaba.excel.exception.ExcelGenerateException; -import com.alibaba.excel.metadata.BaseRowModel; -import com.alibaba.excel.metadata.CellData; -import com.alibaba.excel.metadata.Head; -import com.alibaba.excel.metadata.property.ExcelContentProperty; -import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; -import com.alibaba.excel.util.WorkBookUtil; -import com.alibaba.excel.write.handler.CellWriteHandler; -import com.alibaba.excel.write.handler.RowWriteHandler; -import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.write.executor.ExcelWriteAddExecutor; +import com.alibaba.excel.write.executor.ExcelWriteFillExecutor; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteWorkbook; -import com.alibaba.excel.write.metadata.holder.WriteHolder; -import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; - -import net.sf.cglib.beans.BeanMap; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * @author jipengfei @@ -48,6 +22,8 @@ import net.sf.cglib.beans.BeanMap; public class ExcelBuilderImpl implements ExcelBuilder { private WriteContext context; + private ExcelWriteFillExecutor excelWriteFillExecutor; + private ExcelWriteAddExecutor excelWriteAddExecutor; public ExcelBuilderImpl(WriteWorkbook writeWorkbook) { try { @@ -63,23 +39,6 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } - private void doAddContent(List data) { - if (CollectionUtils.isEmpty(data)) { - return; - } - WriteSheetHolder writeSheetHolder = context.writeSheetHolder(); - int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); - if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) { - newRowIndex += context.currentWriteHolder().relativeHeadRowIndex(); - } - // BeanMap is out of order,so use fieldList - List fieldList = new ArrayList(); - for (int relativeRowIndex = 0; relativeRowIndex < data.size(); relativeRowIndex++) { - int n = relativeRowIndex + newRowIndex; - addOneRowOfDataToExcel(data.get(relativeRowIndex), n, relativeRowIndex, fieldList); - } - } - @Override public void addContent(List data, WriteSheet writeSheet) { addContent(data, writeSheet, null); @@ -88,9 +47,15 @@ public class ExcelBuilderImpl implements ExcelBuilder { @Override public void addContent(List data, WriteSheet writeSheet, WriteTable writeTable) { try { - context.currentSheet(writeSheet); + if (data == null) { + return; + } + context.currentSheet(writeSheet, WriteTypeEnum.ADD); context.currentTable(writeTable); - doAddContent(data); + if (excelWriteAddExecutor == null) { + excelWriteAddExecutor = new ExcelWriteAddExecutor(context); + } + excelWriteAddExecutor.add(data); } catch (RuntimeException e) { finish(); throw e; @@ -101,278 +66,64 @@ public class ExcelBuilderImpl implements ExcelBuilder { } @Override - public void finish() { - if (context != null) { - context.finish(); - } - } - - @Override - public void merge(int firstRow, int lastRow, int firstCol, int lastCol) { - CellRangeAddress cra = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol); - context.writeSheetHolder().getSheet().addMergedRegion(cra); - } - - @Override - public WriteContext writeContext() { - return context; - } - - private void addOneRowOfDataToExcel(Object oneRowData, int n, int relativeRowIndex, List fieldList) { - beforeRowCreate(n, relativeRowIndex); - Row row = WorkBookUtil.createRow(context.writeSheetHolder().getSheet(), n); - afterRowCreate(row, relativeRowIndex); - if (oneRowData instanceof List) { - addBasicTypeToExcel((List)oneRowData, row, relativeRowIndex); - } else { - addJavaObjectToExcel(oneRowData, row, relativeRowIndex, fieldList); - } - } - - private void beforeRowCreate(int rowIndex, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).beforeRowCreate(context.writeSheetHolder(), context.writeTableHolder(), - rowIndex, relativeRowIndex, false); - } - } - } - - private void afterRowCreate(Row row, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).afterRowCreate(context.writeSheetHolder(), context.writeTableHolder(), - row, relativeRowIndex, false); - } - } - if (null != context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { - context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().row(row.getRowNum(), row); - } - } - - private void addBasicTypeToExcel(List oneRowData, Row row, int relativeRowIndex) { - if (CollectionUtils.isEmpty(oneRowData)) { - return; - } - Map headMap = context.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); - int dataIndex = 0; - int cellIndex = 0; - for (Map.Entry entry : headMap.entrySet()) { - if (dataIndex >= oneRowData.size()) { + public void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { + try { + if (data == null) { return; } - cellIndex = entry.getKey(); - Head head = entry.getValue(); - doAddBasicTypeToExcel(oneRowData, head, row, relativeRowIndex, dataIndex++, cellIndex); - } - // Finish - if (dataIndex >= oneRowData.size()) { - return; - } - if (cellIndex != 0) { - cellIndex++; - } - int size = oneRowData.size() - dataIndex; - for (int i = 0; i < size; i++) { - doAddBasicTypeToExcel(oneRowData, null, row, relativeRowIndex, dataIndex++, cellIndex++); - } - } - - private void doAddBasicTypeToExcel(List oneRowData, Head head, Row row, int relativeRowIndex, int dataIndex, - int cellIndex) { - beforeCellCreate(row, head, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, cellIndex); - Object value = oneRowData.get(dataIndex); - CellData cellData = - converterAndSet(context.currentWriteHolder(), value == null ? null : value.getClass(), cell, value, null); - afterCellCreate(head, cellData, cell, relativeRowIndex); - } - - private void addJavaObjectToExcel(Object oneRowData, Row row, int relativeRowIndex, List fieldList) { - WriteHolder currentWriteHolder = context.currentWriteHolder(); - BeanMap beanMap = BeanMap.create(oneRowData); - Set beanMapHandledSet = new HashSet(); - int cellIndex = 0; - // If it's a class it needs to be cast by type - if (HeadKindEnum.CLASS.equals(context.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) { - Map headMap = context.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); - Map contentPropertyMap = - context.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap(); - for (Map.Entry entry : contentPropertyMap.entrySet()) { - cellIndex = entry.getKey(); - ExcelContentProperty excelContentProperty = entry.getValue(); - String name = excelContentProperty.getField().getName(); - if (!beanMap.containsKey(name)) { - continue; - } - Head head = headMap.get(cellIndex); - beforeCellCreate(row, head, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, cellIndex); - Object value = beanMap.get(name); - CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, - value, excelContentProperty); - afterCellCreate(head, cellData, cell, relativeRowIndex); - beanMapHandledSet.add(name); - } - } - // Finish - if (beanMapHandledSet.size() == beanMap.size()) { - return; - } - if (cellIndex != 0) { - cellIndex++; - } - Map ignoreMap = context.currentWriteHolder().excelWriteHeadProperty().getIgnoreMap(); - initFieldList(oneRowData.getClass(), fieldList); - for (Field field : fieldList) { - String filedName = field.getName(); - boolean uselessData = !beanMap.containsKey(filedName) || beanMapHandledSet.contains(filedName) - || ignoreMap.containsKey(filedName); - if (uselessData) { - continue; + if (context.writeWorkbookHolder().getTempTemplateInputStream() == null) { + throw new ExcelGenerateException("Calling the 'fill' method must use a template."); } - Object value = beanMap.get(filedName); - if (value == null) { - continue; + context.currentSheet(writeSheet, WriteTypeEnum.FILL); + if (excelWriteFillExecutor == null) { + excelWriteFillExecutor = new ExcelWriteFillExecutor(context); } - beforeCellCreate(row, null, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, cellIndex++); - CellData cellData = converterAndSet(currentWriteHolder, value.getClass(), cell, value, null); - afterCellCreate(null, cellData, cell, relativeRowIndex); + excelWriteFillExecutor.fill(data, fillConfig); + } catch (RuntimeException e) { + finish(); + throw e; + } catch (Throwable e) { + finish(); + throw new ExcelGenerateException(e); } } - private void initFieldList(Class clazz, List fieldList) { - if (!fieldList.isEmpty()) { - return; - } - Class tempClass = clazz; - while (tempClass != null) { - if (tempClass != BaseRowModel.class) { - Collections.addAll(fieldList, tempClass.getDeclaredFields()); - } - tempClass = tempClass.getSuperclass(); + @Override + public void finish() { + if (context != null) { + context.finish(); } } - private void beforeCellCreate(Row row, Head head, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).beforeCellCreate(context.writeSheetHolder(), - context.writeTableHolder(), row, head, relativeRowIndex, false); + @Override + public void addContent(List data, WriteSheet writeSheet, WriteTable writeTable, String password) { + try { + if (data == null) { + return; } - } - - } - - private void afterCellCreate(Head head, CellData cellData, Cell cell, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).afterCellCreate(context.writeSheetHolder(), context.writeTableHolder(), - cellData, cell, head, relativeRowIndex, false); + context.currentSheet(writeSheet, WriteTypeEnum.ADD); + context.currentTable(writeTable); + if (excelWriteAddExecutor == null) { + excelWriteAddExecutor = new ExcelWriteAddExecutor(context); } - } - if (null != context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { - context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().cell(cell.getRowIndex(), cell); - } - } - - private CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, - ExcelContentProperty excelContentProperty) { - if (value == null) { - return null; - } - if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) { - value = ((String)value).trim(); - } - CellData cellData = convert(currentWriteHolder, clazz, cell, value, excelContentProperty); - if (cellData == null || cellData.getType() == null) { - throw new ExcelDataConvertException( - "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum()); - } - if (cellData.getFormula() != null && cellData.getFormula()) { - cell.setCellFormula(cellData.getFormulaValue()); - } - switch (cellData.getType()) { - case STRING: - cell.setCellValue(cellData.getStringValue()); - return cellData; - case BOOLEAN: - cell.setCellValue(cellData.getBooleanValue()); - return cellData; - case NUMBER: - cell.setCellValue(cellData.getNumberValue().doubleValue()); - return cellData; - case IMAGE: - setImageValue(cellData, cell); - return cellData; - default: - throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() - + "at row:" + cell.getRow().getRowNum()); + excelWriteAddExecutor.add(data); + } catch (RuntimeException e) { + finish(); + throw e; + } catch (Throwable e) { + finish(); + throw new ExcelGenerateException(e); } } - private void setImageValue(CellData cellData, Cell cell) { - Sheet sheet = cell.getSheet(); - int index = sheet.getWorkbook().addPicture(cellData.getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); - Drawing drawing = sheet.getDrawingPatriarch(); - if (drawing == null) { - drawing = sheet.createDrawingPatriarch(); - } - CreationHelper helper = sheet.getWorkbook().getCreationHelper(); - ClientAnchor anchor = helper.createClientAnchor(); - anchor.setDx1(0); - anchor.setDx2(0); - anchor.setDy1(0); - anchor.setDy2(0); - anchor.setCol1(cell.getColumnIndex()); - anchor.setCol2(cell.getColumnIndex() + 1); - anchor.setRow1(cell.getRowIndex()); - anchor.setRow2(cell.getRowIndex() + 1); - anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); - drawing.createPicture(anchor, index); + @Override + public void merge(int firstRow, int lastRow, int firstCol, int lastCol) { + CellRangeAddress cra = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol); + context.writeSheetHolder().getSheet().addMergedRegion(cra); } - private CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, - ExcelContentProperty excelContentProperty) { - if (value instanceof CellData) { - return (CellData)value; - } - Converter converter = null; - if (excelContentProperty != null) { - converter = excelContentProperty.getConverter(); - } - if (converter == null) { - converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz)); - } - if (converter == null) { - throw new ExcelDataConvertException( - "Can not find 'Converter' support class " + clazz.getSimpleName() + "."); - } - CellData cellData; - try { - cellData = - converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration()); - } catch (Exception e) { - throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), - e); - } - return cellData; + @Override + public WriteContext writeContext() { + return context; } } diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java index 5e699c32..82de0381 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java @@ -79,6 +79,30 @@ public class ExcelWriterBuilder { return this; } + /** + * Use the default style.Default is true. + * + * @param useDefaultStyle + * @return + */ + public ExcelWriterBuilder useDefaultStyle(Boolean useDefaultStyle) { + writeWorkbook.setUseDefaultStyle(useDefaultStyle); + return this; + } + + /** + * Whether the encryption. + *

+ * WARRING:Encryption is when the entire file is read into memory, so it is very memory intensive. + * + * @param password + * @return + */ + public ExcelWriterBuilder password(String password) { + writeWorkbook.setPassword(password); + return this; + } + /** * The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a * field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed. diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java index bab837d6..26acff51 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -8,6 +8,7 @@ import com.alibaba.excel.converters.Converter; import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * Build sheet @@ -73,6 +74,17 @@ public class ExcelWriterSheetBuilder { return this; } + /** + * Use the default style.Default is true. + * + * @param useDefaultStyle + * @return + */ + public ExcelWriterSheetBuilder useDefaultStyle(Boolean useDefaultStyle) { + writeSheet.setUseDefaultStyle(useDefaultStyle); + return this; + } + /** * Custom type conversions override the default. * @@ -135,6 +147,18 @@ public class ExcelWriterSheetBuilder { excelWriter.finish(); } + public void doFill(Object data) { + doFill(data, null); + } + + public void doFill(Object data, FillConfig fillConfig) { + if (excelWriter == null) { + throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method"); + } + excelWriter.fill(data, fillConfig, build()); + excelWriter.finish(); + } + public ExcelWriterTableBuilder table() { return table(null); } diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java index 14f755ab..eca1fe07 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java @@ -78,6 +78,17 @@ public class ExcelWriterTableBuilder { return this; } + /** + * Use the default style.Default is true. + * + * @param useDefaultStyle + * @return + */ + public ExcelWriterTableBuilder useDefaultStyle(Boolean useDefaultStyle) { + writeTable.setUseDefaultStyle(useDefaultStyle); + return this; + } + /** * Custom type conversions override the default. * diff --git a/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java b/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java new file mode 100644 index 00000000..9adcb625 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java @@ -0,0 +1,140 @@ +package com.alibaba.excel.write.executor; + +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.Sheet; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ConverterKeyBuild; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.write.metadata.holder.WriteHolder; + +/** + * Excel write Executor + * + * @author Jiaju Zhuang + */ +public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor { + protected WriteContext writeContext; + + public AbstractExcelWriteExecutor(WriteContext writeContext) { + this.writeContext = writeContext; + } + + protected CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { + if (value == null) { + return new CellData(); + } + if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) { + value = ((String)value).trim(); + } + CellData cellData = convert(currentWriteHolder, clazz, cell, value, excelContentProperty); + if (cellData.getFormula() != null && cellData.getFormula()) { + cell.setCellFormula(cellData.getFormulaValue()); + } + switch (cellData.getType()) { + case STRING: + cell.setCellValue(cellData.getStringValue()); + return cellData; + case BOOLEAN: + cell.setCellValue(cellData.getBooleanValue()); + return cellData; + case NUMBER: + cell.setCellValue(cellData.getNumberValue().doubleValue()); + return cellData; + case IMAGE: + setImageValue(cellData, cell); + return cellData; + case EMPTY: + return cellData; + default: + throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() + + "at row:" + cell.getRow().getRowNum()); + } + } + + protected CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { + if (value == null) { + return new CellData(); + } + // This means that the user has defined the data. + if (value instanceof CellData) { + CellData cellDataValue = (CellData)value; + if (cellDataValue.getType() != null) { + return cellDataValue; + } else { + if (cellDataValue.getData() == null) { + cellDataValue.setType(CellDataTypeEnum.EMPTY); + return cellDataValue; + } + } + CellData cellDataReturn = doConvert(currentWriteHolder, cellDataValue.getData().getClass(), cell, + cellDataValue.getData(), excelContentProperty); + // The formula information is subject to user input + if (cellDataValue.getFormula() != null) { + cellDataReturn.setFormula(cellDataValue.getFormula()); + cellDataReturn.setFormulaValue(cellDataValue.getFormulaValue()); + } + return cellDataReturn; + } + return doConvert(currentWriteHolder, clazz, cell, value, excelContentProperty); + } + + private CellData doConvert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { + Converter converter = null; + if (excelContentProperty != null) { + converter = excelContentProperty.getConverter(); + } + if (converter == null) { + converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz)); + } + if (converter == null) { + throw new ExcelDataConvertException( + "Can not find 'Converter' support class " + clazz.getSimpleName() + "."); + } + CellData cellData; + try { + cellData = + converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration()); + } catch (Exception e) { + throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), + e); + } + if (cellData == null || cellData.getType() == null) { + throw new ExcelDataConvertException( + "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum()); + } + return cellData; + } + + private void setImageValue(CellData cellData, Cell cell) { + Sheet sheet = cell.getSheet(); + int index = sheet.getWorkbook().addPicture(cellData.getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); + Drawing drawing = sheet.getDrawingPatriarch(); + if (drawing == null) { + drawing = sheet.createDrawingPatriarch(); + } + CreationHelper helper = sheet.getWorkbook().getCreationHelper(); + ClientAnchor anchor = helper.createClientAnchor(); + anchor.setDx1(0); + anchor.setDx2(0); + anchor.setDy1(0); + anchor.setDy2(0); + anchor.setCol1(cell.getColumnIndex()); + anchor.setCol2(cell.getColumnIndex() + 1); + anchor.setRow1(cell.getRowIndex()); + anchor.setRow2(cell.getRowIndex() + 1); + anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); + drawing.createPicture(anchor, index); + } +} diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java new file mode 100644 index 00000000..8843e2cc --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java @@ -0,0 +1,179 @@ +package com.alibaba.excel.write.executor; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.metadata.BaseRowModel; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.CollectionUtils; +import com.alibaba.excel.util.WorkBookUtil; +import com.alibaba.excel.util.WriteHandlerUtils; +import com.alibaba.excel.write.metadata.holder.WriteHolder; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import net.sf.cglib.beans.BeanMap; + +/** + * Add the data into excel + * + * @author Jiaju Zhuang + */ +public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { + + public ExcelWriteAddExecutor(WriteContext writeContext) { + super(writeContext); + } + + public void add(List data) { + if (CollectionUtils.isEmpty(data)) { + return; + } + WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder(); + int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); + if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) { + newRowIndex += writeContext.currentWriteHolder().relativeHeadRowIndex(); + } + // BeanMap is out of order,so use fieldList + List fieldList = new ArrayList(); + for (int relativeRowIndex = 0; relativeRowIndex < data.size(); relativeRowIndex++) { + int n = relativeRowIndex + newRowIndex; + addOneRowOfDataToExcel(data.get(relativeRowIndex), n, relativeRowIndex, fieldList); + } + } + + private void addOneRowOfDataToExcel(Object oneRowData, int n, int relativeRowIndex, List fieldList) { + if (oneRowData == null) { + return; + } + WriteHandlerUtils.beforeRowCreate(writeContext, n, relativeRowIndex, Boolean.FALSE); + Row row = WorkBookUtil.createRow(writeContext.writeSheetHolder().getSheet(), n); + WriteHandlerUtils.afterRowCreate(writeContext, row, relativeRowIndex, Boolean.FALSE); + if (oneRowData instanceof List) { + addBasicTypeToExcel((List)oneRowData, row, relativeRowIndex); + } else { + addJavaObjectToExcel(oneRowData, row, relativeRowIndex, fieldList); + } + WriteHandlerUtils.afterRowDispose(writeContext, row, relativeRowIndex, Boolean.FALSE); + } + + private void addBasicTypeToExcel(List oneRowData, Row row, int relativeRowIndex) { + if (CollectionUtils.isEmpty(oneRowData)) { + return; + } + Map headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); + int dataIndex = 0; + int cellIndex = 0; + for (Map.Entry entry : headMap.entrySet()) { + if (dataIndex >= oneRowData.size()) { + return; + } + cellIndex = entry.getKey(); + Head head = entry.getValue(); + doAddBasicTypeToExcel(oneRowData, head, row, relativeRowIndex, dataIndex++, cellIndex); + } + // Finish + if (dataIndex >= oneRowData.size()) { + return; + } + if (cellIndex != 0) { + cellIndex++; + } + int size = oneRowData.size() - dataIndex; + for (int i = 0; i < size; i++) { + doAddBasicTypeToExcel(oneRowData, null, row, relativeRowIndex, dataIndex++, cellIndex++); + } + } + + private void doAddBasicTypeToExcel(List oneRowData, Head head, Row row, int relativeRowIndex, int dataIndex, + int cellIndex) { + WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE); + Cell cell = WorkBookUtil.createCell(row, cellIndex); + WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE); + Object value = oneRowData.get(dataIndex); + CellData cellData = converterAndSet(writeContext.currentWriteHolder(), value == null ? null : value.getClass(), + cell, value, null); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE); + } + + private void addJavaObjectToExcel(Object oneRowData, Row row, int relativeRowIndex, List fieldList) { + WriteHolder currentWriteHolder = writeContext.currentWriteHolder(); + BeanMap beanMap = BeanMap.create(oneRowData); + Set beanMapHandledSet = new HashSet(); + int cellIndex = 0; + // If it's a class it needs to be cast by type + if (HeadKindEnum.CLASS.equals(writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) { + Map headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); + Map contentPropertyMap = + writeContext.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap(); + for (Map.Entry entry : contentPropertyMap.entrySet()) { + cellIndex = entry.getKey(); + ExcelContentProperty excelContentProperty = entry.getValue(); + String name = excelContentProperty.getField().getName(); + if (!beanMap.containsKey(name)) { + continue; + } + Head head = headMap.get(cellIndex); + WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE); + Cell cell = WorkBookUtil.createCell(row, cellIndex); + WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE); + Object value = beanMap.get(name); + CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, + value, excelContentProperty); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE); + beanMapHandledSet.add(name); + } + } + // Finish + if (beanMapHandledSet.size() == beanMap.size()) { + return; + } + if (cellIndex != 0) { + cellIndex++; + } + Map ignoreMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getIgnoreMap(); + initFieldList(oneRowData.getClass(), fieldList); + for (Field field : fieldList) { + String filedName = field.getName(); + boolean uselessData = !beanMap.containsKey(filedName) || beanMapHandledSet.contains(filedName) + || ignoreMap.containsKey(filedName); + if (uselessData) { + continue; + } + Object value = beanMap.get(filedName); + if (value == null) { + continue; + } + WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE); + Cell cell = WorkBookUtil.createCell(row, cellIndex++); + WriteHandlerUtils.afterCellCreate(writeContext, cell, null, relativeRowIndex, Boolean.FALSE); + CellData cellData = converterAndSet(currentWriteHolder, value.getClass(), cell, value, null); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE); + } + } + + private void initFieldList(Class clazz, List fieldList) { + if (!fieldList.isEmpty()) { + return; + } + Class tempClass = clazz; + while (tempClass != null) { + if (tempClass != BaseRowModel.class) { + Collections.addAll(fieldList, tempClass.getDeclaredFields()); + } + tempClass = tempClass.getSuperclass(); + } + } + +} diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java new file mode 100644 index 00000000..b7cb607c --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java @@ -0,0 +1,9 @@ +package com.alibaba.excel.write.executor; + +/** + * Excel write Executor + * + * @author Jiaju Zhuang + */ +public interface ExcelWriteExecutor { +} diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java new file mode 100644 index 00000000..217f21e5 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java @@ -0,0 +1,377 @@ +package com.alibaba.excel.write.executor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.CollectionUtils; +import com.alibaba.excel.util.StringUtils; +import com.alibaba.excel.util.WriteHandlerUtils; +import com.alibaba.excel.write.metadata.fill.AnalysisCell; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import net.sf.cglib.beans.BeanMap; + +/** + * Fill the data into excel + * + * @author Jiaju Zhuang + */ +public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { + + private static final String ESCAPE_FILL_PREFIX = "\\\\\\{"; + private static final String ESCAPE_FILL_SUFFIX = "\\\\\\}"; + private static final String FILL_PREFIX = "{"; + private static final String FILL_SUFFIX = "}"; + private static final char IGNORE_CHAR = '\\'; + private static final String COLLECTION_PREFIX = "."; + /** + * Fields to replace in the template + */ + private Map> templateAnalysisCache = new HashMap>(8); + /** + * Collection fields to replace in the template + */ + private Map> templateCollectionAnalysisCache = + new HashMap>(8); + /** + * Style cache for collection fields + */ + private Map> collectionFieldStyleCache = + new HashMap>(8); + /** + * Last index cache for collection fields + */ + private Map> collectionLastIndexCache = + new HashMap>(8); + + public ExcelWriteFillExecutor(WriteContext writeContext) { + super(writeContext); + } + + public void fill(Object data, FillConfig fillConfig) { + if (fillConfig == null) { + fillConfig = FillConfig.builder().build(true); + } + fillConfig.init(); + if (data instanceof Collection) { + List analysisCellList = readTemplateData(templateCollectionAnalysisCache); + Collection collectionData = (Collection)data; + if (CollectionUtils.isEmpty(collectionData)) { + return; + } + Iterator iterator = collectionData.iterator(); + if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) { + shiftRows(collectionData.size(), analysisCellList); + } + while (iterator.hasNext()) { + doFill(analysisCellList, iterator.next(), fillConfig); + } + } else { + doFill(readTemplateData(templateAnalysisCache), data, fillConfig); + } + } + + private void shiftRows(int size, List analysisCellList) { + if (CollectionUtils.isEmpty(analysisCellList)) { + return; + } + int maxRowIndex = 0; + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); + Map collectionLastIndexMap = collectionLastIndexCache.get(sheetNo); + for (AnalysisCell analysisCell : analysisCellList) { + if (collectionLastIndexMap != null) { + Integer lastRowIndex = collectionLastIndexMap.get(analysisCell); + if (lastRowIndex != null) { + if (lastRowIndex > maxRowIndex) { + maxRowIndex = lastRowIndex; + } + continue; + } + } + if (analysisCell.getRowIndex() > maxRowIndex) { + maxRowIndex = analysisCell.getRowIndex(); + } + } + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + int lastRowIndex = cachedSheet.getLastRowNum(); + if (maxRowIndex >= lastRowIndex) { + return; + } + Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); + int number = size; + if (collectionLastIndexMap == null) { + number--; + } + sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number); + for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) { + if (analysisCell.getRowIndex() > maxRowIndex) { + analysisCell.setRowIndex(analysisCell.getRowIndex() + number); + } + } + } + + private void doFill(List analysisCellList, Object oneRowData, FillConfig fillConfig) { + Map dataMap; + if (oneRowData instanceof Map) { + dataMap = (Map)oneRowData; + } else { + dataMap = BeanMap.create(oneRowData); + } + WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder(); + Map fieldNameContentPropertyMap = + writeContext.currentWriteHolder().excelWriteHeadProperty().getFieldNameContentPropertyMap(); + for (AnalysisCell analysisCell : analysisCellList) { + Cell cell = getOneCell(analysisCell, fillConfig); + if (analysisCell.getOnlyOneVariable()) { + String variable = analysisCell.getVariableList().get(0); + if (!dataMap.containsKey(variable)) { + continue; + } + Object value = dataMap.get(variable); + CellData cellData = converterAndSet(writeSheetHolder, value == null ? null : value.getClass(), cell, + value, fieldNameContentPropertyMap.get(variable)); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, null, Boolean.FALSE); + } else { + StringBuilder cellValueBuild = new StringBuilder(); + int index = 0; + List cellDataList = new ArrayList(); + for (String variable : analysisCell.getVariableList()) { + cellValueBuild.append(analysisCell.getPrepareDataList().get(index++)); + if (!dataMap.containsKey(variable)) { + continue; + } + Object value = dataMap.get(variable); + CellData cellData = convert(writeSheetHolder, value == null ? null : value.getClass(), cell, value, + fieldNameContentPropertyMap.get(variable)); + cellDataList.add(cellData); + switch (cellData.getType()) { + case STRING: + cellValueBuild.append(cellData.getStringValue()); + break; + case BOOLEAN: + cellValueBuild.append(cellData.getBooleanValue()); + break; + case NUMBER: + cellValueBuild.append(cellData.getNumberValue()); + break; + default: + break; + } + } + cellValueBuild.append(analysisCell.getPrepareDataList().get(index)); + cell.setCellValue(cellValueBuild.toString()); + WriteHandlerUtils.afterCellDispose(writeContext, cellDataList, cell, null, null, Boolean.FALSE); + } + } + } + + private Cell getOneCell(AnalysisCell analysisCell, FillConfig fillConfig) { + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { + return cachedSheet.getRow(analysisCell.getRowIndex()).getCell(analysisCell.getColumnIndex()); + } + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + + Map collectionLastIndexMap = collectionLastIndexCache.get(sheetNo); + if (collectionLastIndexMap == null) { + collectionLastIndexMap = new HashMap(16); + collectionLastIndexCache.put(sheetNo, collectionLastIndexMap); + } + boolean isOriginalCell = false; + Integer lastRowIndex; + Integer lastColumnIndex; + switch (fillConfig.getDirection()) { + case VERTICAL: + lastRowIndex = collectionLastIndexMap.get(analysisCell); + if (lastRowIndex == null) { + lastRowIndex = analysisCell.getRowIndex(); + collectionLastIndexMap.put(analysisCell, lastRowIndex); + isOriginalCell = true; + } else { + collectionLastIndexMap.put(analysisCell, ++lastRowIndex); + } + lastColumnIndex = analysisCell.getColumnIndex(); + break; + case HORIZONTAL: + lastRowIndex = analysisCell.getRowIndex(); + lastColumnIndex = collectionLastIndexMap.get(analysisCell); + if (lastColumnIndex == null) { + lastColumnIndex = analysisCell.getColumnIndex(); + collectionLastIndexMap.put(analysisCell, lastColumnIndex); + isOriginalCell = true; + } else { + collectionLastIndexMap.put(analysisCell, ++lastColumnIndex); + } + break; + default: + throw new ExcelGenerateException("The wrong direction."); + } + Row row = sheet.getRow(lastRowIndex); + if (row == null) { + row = cachedSheet.getRow(lastRowIndex); + if (row == null) { + WriteHandlerUtils.beforeRowCreate(writeContext, lastRowIndex, null, Boolean.FALSE); + if (fillConfig.getForceNewRow()) { + row = cachedSheet.createRow(lastRowIndex); + } else { + row = sheet.createRow(lastRowIndex); + } + WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE); + } + } + Cell cell = row.getCell(lastColumnIndex); + if (cell == null) { + WriteHandlerUtils.beforeCellCreate(writeContext, row, null, lastColumnIndex, null, Boolean.FALSE); + cell = row.createCell(lastColumnIndex); + WriteHandlerUtils.afterCellCreate(writeContext, cell, null, null, Boolean.FALSE); + } + + Map collectionFieldStyleMap = collectionFieldStyleCache.get(sheetNo); + if (collectionFieldStyleMap == null) { + collectionFieldStyleMap = new HashMap(16); + collectionFieldStyleCache.put(sheetNo, collectionFieldStyleMap); + } + if (isOriginalCell) { + collectionFieldStyleMap.put(analysisCell, cell.getCellStyle()); + } else { + CellStyle cellStyle = collectionFieldStyleMap.get(analysisCell); + if (cellStyle != null) { + cell.setCellStyle(cellStyle); + } + } + return cell; + } + + private List readTemplateData(Map> analysisCache) { + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); + List analysisCellList = analysisCache.get(sheetNo); + if (analysisCellList != null) { + return analysisCellList; + } + Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); + analysisCellList = new ArrayList(); + List collectionAnalysisCellList = new ArrayList(); + for (int i = 0; i <= sheet.getLastRowNum(); i++) { + Row row = sheet.getRow(i); + if (row == null) { + continue; + } + for (int j = 0; j < row.getLastCellNum(); j++) { + Cell cell = row.getCell(j); + if (cell == null) { + continue; + } + prepareData(cell.getStringCellValue(), analysisCellList, collectionAnalysisCellList, i, j); + } + } + templateAnalysisCache.put(sheetNo, analysisCellList); + templateCollectionAnalysisCache.put(sheetNo, collectionAnalysisCellList); + return analysisCache.get(sheetNo); + } + + private void prepareData(String value, List analysisCellList, + List collectionAnalysisCellList, int rowIndex, int columnIndex) { + if (StringUtils.isEmpty(value)) { + return; + } + AnalysisCell analysisCell = null; + int startIndex = 0; + int length = value.length(); + int lastPrepareDataIndex = 0; + out: + while (startIndex < length) { + int prefixIndex = value.indexOf(FILL_PREFIX, startIndex); + if (prefixIndex < 0) { + break out; + } + if (prefixIndex != 0) { + char prefixPrefixChar = value.charAt(prefixIndex - 1); + if (prefixPrefixChar == IGNORE_CHAR) { + startIndex = prefixIndex + 1; + continue; + } + } + int suffixIndex = -1; + while (suffixIndex == -1 && startIndex < length) { + suffixIndex = value.indexOf(FILL_SUFFIX, startIndex + 1); + if (suffixIndex < 0) { + break out; + } + startIndex = suffixIndex + 1; + char prefixSuffixChar = value.charAt(suffixIndex - 1); + if (prefixSuffixChar == IGNORE_CHAR) { + suffixIndex = -1; + } + } + if (analysisCell == null) { + analysisCell = new AnalysisCell(); + analysisCell.setRowIndex(rowIndex); + analysisCell.setColumnIndex(columnIndex); + analysisCell.setOnlyOneVariable(Boolean.TRUE); + List variableList = new ArrayList(); + analysisCell.setVariableList(variableList); + List prepareDataList = new ArrayList(); + analysisCell.setPrepareDataList(prepareDataList); + analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON); + } + String variable = value.substring(prefixIndex + 1, suffixIndex); + if (StringUtils.isEmpty(variable)) { + continue; + } + if (variable.startsWith(COLLECTION_PREFIX)) { + variable = variable.substring(1); + if (StringUtils.isEmpty(variable)) { + continue; + } + analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COLLECTION); + } + analysisCell.getVariableList().add(variable); + if (lastPrepareDataIndex == prefixIndex) { + analysisCell.getPrepareDataList().add(StringUtils.EMPTY); + } else { + analysisCell.getPrepareDataList() + .add(convertPrepareData(value.substring(lastPrepareDataIndex, prefixIndex))); + analysisCell.setOnlyOneVariable(Boolean.FALSE); + } + lastPrepareDataIndex = suffixIndex + 1; + } + if (analysisCell != null) { + if (lastPrepareDataIndex == length) { + analysisCell.getPrepareDataList().add(StringUtils.EMPTY); + } else { + analysisCell.getPrepareDataList().add(convertPrepareData(value.substring(lastPrepareDataIndex))); + analysisCell.setOnlyOneVariable(Boolean.FALSE); + } + if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { + analysisCellList.add(analysisCell); + } else { + collectionAnalysisCellList.add(analysisCell); + } + } + } + + private String convertPrepareData(String prepareData) { + prepareData = prepareData.replaceAll(ESCAPE_FILL_PREFIX, FILL_PREFIX); + prepareData = prepareData.replaceAll(ESCAPE_FILL_SUFFIX, FILL_SUFFIX); + return prepareData; + } + +} diff --git a/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java index 9e8af22b..4d981380 100644 --- a/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.handler; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; @@ -16,32 +18,56 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; public interface CellWriteHandler extends WriteHandler { /** - * called before create the cell + * Called before create the cell * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param row * @param head + * Nullable.It is null in the case of fill data and without head. + * @param columnIndex * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * It will always be false when fill data. */ void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, - int relativeRowIndex, boolean isHead); + Integer columnIndex, Integer relativeRowIndex, Boolean isHead); + + /** + * Called after the cell is created + * + * @param writeSheetHolder + * @param writeTableHolder + * Nullable.It is null without using table writes. + * @param cell + * @param head + * Nullable.It is null in the case of fill data and without head. + * @param relativeRowIndex + * Nullable.It is null in the case of fill data. + * @param isHead + * It will always be false when fill data. + */ + void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead); /** - * called after the cell is created + * Called after all operations on the cell have been completed * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param cell * @param head - * @param cellData - * Nullable. + * Nullable.It is null in the case of fill data and without head. + * @param cellDataList + * Nullable.It is null in the case of add header.There may be several when fill the data. * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * It will always be false when fill data. */ - void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead); + void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java b/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java index f52af6b3..dff84d7a 100644 --- a/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java +++ b/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java @@ -21,16 +21,18 @@ public class DefaultWriteHandlerLoader { * * @return */ - public static List loadDefaultHandler() { + public static List loadDefaultHandler(Boolean useDefaultStyle) { List handlerList = new ArrayList(); - WriteCellStyle headWriteCellStyle = new WriteCellStyle(); - headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); - WriteFont headWriteFont = new WriteFont(); - headWriteFont.setFontName("宋体"); - headWriteFont.setFontHeightInPoints((short)14); - headWriteFont.setBold(true); - headWriteCellStyle.setWriteFont(headWriteFont); - handlerList.add(new HorizontalCellStyleStrategy(headWriteCellStyle, new ArrayList())); + if (useDefaultStyle) { + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + WriteFont headWriteFont = new WriteFont(); + headWriteFont.setFontName("宋体"); + headWriteFont.setFontHeightInPoints((short)14); + headWriteFont.setBold(true); + headWriteCellStyle.setWriteFont(headWriteFont); + handlerList.add(new HorizontalCellStyleStrategy(headWriteCellStyle, new ArrayList())); + } return handlerList; } diff --git a/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java index 1630bd31..29ac10b0 100644 --- a/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java @@ -13,28 +13,47 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; public interface RowWriteHandler extends WriteHandler { /** - * called before create the row + * Called before create the row * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param rowIndex * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * Nullable.It is null in the case of fill data. */ - void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, int rowIndex, - int relativeRowIndex, boolean isHead); + void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead); /** - * called after the row is created + * Called after the row is created * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param row * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * Nullable.It is null in the case of fill data. */ void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - int relativeRowIndex, boolean isHead); + Integer relativeRowIndex, Boolean isHead); + + /** + * Called after all operations on the row have been completed.This method is not called when fill the data. + * + * @param writeSheetHolder + * @param writeTableHolder + * Nullable.It is null without using table writes. + * @param row + * @param relativeRowIndex + * Nullable.It is null in the case of fill data. + * @param isHead + * Nullable.It is null in the case of fill data. + */ + void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java index 080abbf2..b97e1d15 100644 --- a/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java @@ -11,7 +11,7 @@ import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; public interface SheetWriteHandler extends WriteHandler { /** - * called before create the sheet + * Called before create the sheet * * @param writeWorkbookHolder * @param writeSheetHolder @@ -19,7 +19,7 @@ public interface SheetWriteHandler extends WriteHandler { void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder); /** - * called after the sheet is created + * Called after the sheet is created * * @param writeWorkbookHolder * @param writeSheetHolder diff --git a/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java index 508c9c57..8dba9646 100644 --- a/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java @@ -10,14 +10,21 @@ import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; public interface WorkbookWriteHandler extends WriteHandler { /** - * called before create the sheet + * Called before create the workbook */ void beforeWorkbookCreate(); /** - * called after the sheet is created + * Called after the workbook is created * * @param writeWorkbookHolder */ void afterWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder); + + /** + * Called after all operations on the workbook have been completed + * + * @param writeWorkbookHolder + */ + void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder); } diff --git a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java b/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java index 7b95f1c5..13bdac3f 100644 --- a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.merge; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; @@ -18,13 +20,17 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; public abstract class AbstractMergeStrategy implements CellWriteHandler { @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + 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) { if (isHead) { return; } diff --git a/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java b/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java index 3a43ff6a..2689cd98 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java +++ b/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java @@ -24,6 +24,10 @@ public class WriteBasicParameter extends BasicParameter { * Custom type handler override the default */ private List customWriteHandlerList = new ArrayList(); + /** + * Use the default style.Default is true. + */ + private Boolean useDefaultStyle; public Integer getRelativeHeadRowIndex() { return relativeHeadRowIndex; @@ -48,4 +52,12 @@ public class WriteBasicParameter extends BasicParameter { public void setCustomWriteHandlerList(List customWriteHandlerList) { this.customWriteHandlerList = customWriteHandlerList; } + + public Boolean getUseDefaultStyle() { + return useDefaultStyle; + } + + public void setUseDefaultStyle(Boolean useDefaultStyle) { + this.useDefaultStyle = useDefaultStyle; + } } 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 8ea5209f..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,13 +43,20 @@ public class WriteWorkbook extends WriteBasicParameter { */ private File templateFile; /** - * Default true + * Default trueuseDefaultStyle */ private Boolean autoCloseStream; /** * Mandatory use 'inputStream' .Default is false */ private Boolean mandatoryUseInputStream; + /** + * Whether the encryption + *

+ * WARRING:Encryption is when the entire file is read into memory, so it is very memory intensive. + * + */ + private String password; /** * The default is all excel objects.Default is true. *

@@ -140,4 +147,12 @@ public class WriteWorkbook extends WriteBasicParameter { public void setWriteHandler(com.alibaba.excel.event.WriteHandler writeHandler) { this.writeHandler = writeHandler; } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } } diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java new file mode 100644 index 00000000..791e2643 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java @@ -0,0 +1,89 @@ +package com.alibaba.excel.write.metadata.fill; + +import java.util.List; + +import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; + +/** + * Read the cells of the template while populating the data. + * + * @author Jiaju Zhuang + **/ +public class AnalysisCell { + private int columnIndex; + private int rowIndex; + private List variableList; + private List prepareDataList; + private Boolean onlyOneVariable; + private WriteTemplateAnalysisCellTypeEnum cellType; + + public int getColumnIndex() { + return columnIndex; + } + + public void setColumnIndex(int columnIndex) { + this.columnIndex = columnIndex; + } + + public int getRowIndex() { + return rowIndex; + } + + public void setRowIndex(int rowIndex) { + this.rowIndex = rowIndex; + } + + public List getVariableList() { + return variableList; + } + + public void setVariableList(List variableList) { + this.variableList = variableList; + } + + public List getPrepareDataList() { + return prepareDataList; + } + + public void setPrepareDataList(List prepareDataList) { + this.prepareDataList = prepareDataList; + } + + public Boolean getOnlyOneVariable() { + return onlyOneVariable; + } + + public void setOnlyOneVariable(Boolean onlyOneVariable) { + this.onlyOneVariable = onlyOneVariable; + } + + public WriteTemplateAnalysisCellTypeEnum getCellType() { + return cellType; + } + + public void setCellType(WriteTemplateAnalysisCellTypeEnum cellType) { + this.cellType = cellType; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnalysisCell that = (AnalysisCell)o; + if (columnIndex != that.columnIndex) { + return false; + } + return rowIndex == that.rowIndex; + } + + @Override + public int hashCode() { + int result = columnIndex; + result = 31 * result + rowIndex; + return result; + } +} diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java b/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java new file mode 100644 index 00000000..a5bbe917 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java @@ -0,0 +1,83 @@ +package com.alibaba.excel.write.metadata.fill; + +import com.alibaba.excel.enums.WriteDirectionEnum; + +/** + * Fill config + * + * @author Jiaju Zhuang + **/ +public class FillConfig { + private WriteDirectionEnum direction; + /** + * Create a new row each time you use the list parameter.The default create if necessary. + *

+ * Warnning:If you use forceNewRow set true, will not be able to use asynchronous write file, simply + * say the whole file will be stored in memory. + */ + private Boolean forceNewRow; + private boolean hasInit; + + public WriteDirectionEnum getDirection() { + return direction; + } + + public void setDirection(WriteDirectionEnum direction) { + this.direction = direction; + } + + public Boolean getForceNewRow() { + return forceNewRow; + } + + public void setForceNewRow(Boolean forceNewRow) { + this.forceNewRow = forceNewRow; + } + + public void init() { + if (hasInit) { + return; + } + if (direction == null) { + direction = WriteDirectionEnum.VERTICAL; + } + if (forceNewRow == null) { + forceNewRow = Boolean.FALSE; + } + hasInit = true; + } + + public static FillConfigBuilder builder() { + return new FillConfigBuilder(); + } + + public static class FillConfigBuilder { + private FillConfig fillConfig; + + FillConfigBuilder() { + this.fillConfig = new FillConfig(); + } + + public FillConfigBuilder direction(WriteDirectionEnum direction) { + fillConfig.setDirection(direction); + return this; + } + + public FillConfigBuilder forceNewRow(Boolean forceNewRow) { + fillConfig.setForceNewRow(forceNewRow); + return this; + } + + public FillConfig build() { + return build(true); + } + + public FillConfig build(boolean autoInit) { + if (autoInit) { + fillConfig.init(); + } + return fillConfig; + } + + } +} diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java index 6592009a..72f5f9be 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java @@ -21,7 +21,6 @@ import com.alibaba.excel.metadata.AbstractHolder; import com.alibaba.excel.metadata.Font; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.TableStyle; -import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.RowHeightProperty; import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.write.handler.CellWriteHandler; @@ -62,6 +61,10 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ * Write handler for workbook */ private Map, List> writeHandlerMap; + /** + * Use the default style.Default is true. + */ + private Boolean useDefaultStyle; public AbstractWriteHolder(WriteBasicParameter writeBasicParameter, AbstractWriteHolder parentAbstractWriteHolder, Boolean convertAllFiled) { @@ -97,6 +100,16 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ this.relativeHeadRowIndex = writeBasicParameter.getRelativeHeadRowIndex(); } + if (writeBasicParameter.getUseDefaultStyle() == null) { + if (parentAbstractWriteHolder == null) { + this.useDefaultStyle = Boolean.TRUE; + } else { + this.useDefaultStyle = parentAbstractWriteHolder.getUseDefaultStyle(); + } + } else { + this.useDefaultStyle = writeBasicParameter.getUseDefaultStyle(); + } + // Initialization property this.excelWriteHeadProperty = new ExcelWriteHeadProperty(getClazz(), getHead(), convertAllFiled); @@ -118,7 +131,7 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ if (parentAbstractWriteHolder != null) { parentWriteHandlerMap = parentAbstractWriteHolder.getWriteHandlerMap(); } else { - handlerList.addAll(DefaultWriteHandlerLoader.loadDefaultHandler()); + handlerList.addAll(DefaultWriteHandlerLoader.loadDefaultHandler(useDefaultStyle)); } this.writeHandlerMap = sortAndClearUpHandler(handlerList, parentWriteHandlerMap); @@ -135,6 +148,7 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ getConverterMap().put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter); } } + } /** @@ -361,6 +375,14 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ this.relativeHeadRowIndex = relativeHeadRowIndex; } + public Boolean getUseDefaultStyle() { + return useDefaultStyle; + } + + public void setUseDefaultStyle(Boolean useDefaultStyle) { + this.useDefaultStyle = useDefaultStyle; + } + @Override public ExcelWriteHeadProperty excelWriteHeadProperty() { return getExcelWriteHeadProperty(); diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java index 7cce9f13..1fb19fb0 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java @@ -3,11 +3,13 @@ package com.alibaba.excel.write.metadata.holder; import java.util.HashMap; import java.util.Map; +import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFSheet; import com.alibaba.excel.enums.HolderEnum; -import com.alibaba.excel.enums.WriteLastRowType; -import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.enums.WriteLastRowTypeEnum; import com.alibaba.excel.write.metadata.WriteSheet; /** @@ -21,9 +23,22 @@ public class WriteSheetHolder extends AbstractWriteHolder { */ private WriteSheet writeSheet; /*** - * poi sheet + * Current poi Sheet.This is only for writing, and there may be no data in version 07 when template data needs to be + * read. + *

    + *
  • 03:{@link HSSFSheet}
  • + *
  • 07:{@link SXSSFSheet}
  • + *
*/ private Sheet sheet; + /*** + * Current poi Sheet.Be sure to use and this method when reading template data. + *
    + *
  • 03:{@link HSSFSheet}
  • + *
  • 07:{@link XSSFSheet}
  • + *
+ */ + private Sheet cachedSheet; /*** * sheetNo */ @@ -47,7 +62,7 @@ public class WriteSheetHolder extends AbstractWriteHolder { * @param writeSheet * @param writeWorkbookHolder */ - private WriteLastRowType writeLastRowType; + private WriteLastRowTypeEnum writeLastRowTypeEnum; public WriteSheetHolder(WriteSheet writeSheet, WriteWorkbookHolder writeWorkbookHolder) { super(writeSheet, writeWorkbookHolder, writeWorkbookHolder.getWriteWorkbook().getConvertAllFiled()); @@ -60,10 +75,10 @@ public class WriteSheetHolder extends AbstractWriteHolder { } this.parentWriteWorkbookHolder = writeWorkbookHolder; this.hasBeenInitializedTable = new HashMap(); - if (writeWorkbookHolder.getTemplateInputStream() == null && writeWorkbookHolder.getTemplateFile() == null) { - writeLastRowType = WriteLastRowType.COMMON_EMPTY; + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + writeLastRowTypeEnum = WriteLastRowTypeEnum.TEMPLATE_EMPTY; } else { - writeLastRowType = WriteLastRowType.TEMPLATE_EMPTY; + writeLastRowTypeEnum = WriteLastRowTypeEnum.COMMON_EMPTY; } } @@ -87,6 +102,14 @@ public class WriteSheetHolder extends AbstractWriteHolder { return sheetNo; } + public Sheet getCachedSheet() { + return cachedSheet; + } + + public void setCachedSheet(Sheet cachedSheet) { + this.cachedSheet = cachedSheet; + } + public void setSheetNo(Integer sheetNo) { this.sheetNo = sheetNo; } @@ -115,12 +138,12 @@ public class WriteSheetHolder extends AbstractWriteHolder { this.hasBeenInitializedTable = hasBeenInitializedTable; } - public WriteLastRowType getWriteLastRowType() { - return writeLastRowType; + public WriteLastRowTypeEnum getWriteLastRowTypeEnum() { + return writeLastRowTypeEnum; } - public void setWriteLastRowType(WriteLastRowType writeLastRowType) { - this.writeLastRowType = writeLastRowType; + public void setWriteLastRowTypeEnum(WriteLastRowTypeEnum writeLastRowTypeEnum) { + this.writeLastRowTypeEnum = writeLastRowTypeEnum; } /** @@ -131,25 +154,16 @@ public class WriteSheetHolder extends AbstractWriteHolder { public int getNewRowIndexAndStartDoWrite() { // 'getLastRowNum' doesn't matter if it has one or zero,is's zero int newRowIndex = 0; - switch (writeLastRowType) { + switch (writeLastRowTypeEnum) { case TEMPLATE_EMPTY: - if (parentWriteWorkbookHolder.getExcelType() == ExcelTypeEnum.XLSX) { - if (parentWriteWorkbookHolder.getTemplateLastRowMap().containsKey(sheetNo)) { - newRowIndex = parentWriteWorkbookHolder.getTemplateLastRowMap().get(sheetNo); - } - } else { - newRowIndex = sheet.getLastRowNum(); - } - newRowIndex++; - break; case HAS_DATA: - newRowIndex = sheet.getLastRowNum(); + newRowIndex = Math.max(sheet.getLastRowNum(), cachedSheet.getLastRowNum()); newRowIndex++; break; default: break; } - writeLastRowType = WriteLastRowType.HAS_DATA; + writeLastRowTypeEnum = WriteLastRowTypeEnum.HAS_DATA; return newRowIndex; } diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java index fe5c395d..d8020852 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -1,19 +1,25 @@ package com.alibaba.excel.write.metadata.holder; -import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.IoUtils; import com.alibaba.excel.write.metadata.WriteWorkbook; /** @@ -23,10 +29,22 @@ import com.alibaba.excel.write.metadata.WriteWorkbook; */ public class WriteWorkbookHolder extends AbstractWriteHolder { /*** - * poi Workbook + * Current poi Workbook.This is only for writing, and there may be no data in version 07 when template data needs to + * be read. + *
    + *
  • 03:{@link HSSFWorkbook}
  • + *
  • 07:{@link SXSSFWorkbook}
  • + *
*/ private Workbook workbook; - + /*** + * Current poi Workbook.Be sure to use and this method when reading template data. + *
    + *
  • 03:{@link HSSFWorkbook}
  • + *
  • 07:{@link XSSFWorkbook}
  • + *
+ */ + private Workbook cachedWorkbook; /** * current param */ @@ -53,6 +71,12 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { * If 'inputStream' and 'file' all not empty,file first */ private File templateFile; + /** + * Temporary template file stream. + *

+ * A temporary file stream needs to be created in order not to modify the original template file. + */ + private InputStream tempTemplateInputStream; /** * Default true */ @@ -70,10 +94,9 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { */ private Map hasBeenInitializedSheet; /** - * When using SXSSFWorkbook, you can't get the actual last line.But we need to read the last line when we are using - * the template, so we cache it + * Whether the encryption */ - private Map templateLastRowMap; + private String password; public WriteWorkbookHolder(WriteWorkbook writeWorkbook) { super(writeWorkbook, null, writeWorkbook.getConvertAllFiled()); @@ -88,19 +111,16 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { } else { this.outputStream = writeWorkbook.getOutputStream(); } - if (writeWorkbook.getTemplateInputStream() != null) { - if (writeWorkbook.getTemplateInputStream().markSupported()) { - this.templateInputStream = writeWorkbook.getTemplateInputStream(); - } else { - this.templateInputStream = new BufferedInputStream(writeWorkbook.getTemplateInputStream()); - } - } - this.templateFile = writeWorkbook.getTemplateFile(); if (writeWorkbook.getAutoCloseStream() == null) { this.autoCloseStream = Boolean.TRUE; } else { this.autoCloseStream = writeWorkbook.getAutoCloseStream(); } + try { + copyTemplate(); + } catch (IOException e) { + throw new ExcelGenerateException("Copy template failure.", e); + } if (writeWorkbook.getExcelType() == null) { if (file != null && file.getName().endsWith(ExcelTypeEnum.XLS.getValue())) { this.excelType = ExcelTypeEnum.XLS; @@ -116,7 +136,26 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.mandatoryUseInputStream = writeWorkbook.getMandatoryUseInputStream(); } this.hasBeenInitializedSheet = new HashMap(); - this.templateLastRowMap = new HashMap(8); + this.password = writeWorkbook.getPassword(); + } + + private void copyTemplate() throws IOException { + if (writeWorkbook.getTemplateFile() == null && writeWorkbook.getTemplateInputStream() == null) { + return; + } + byte[] templateFileByte = null; + if (writeWorkbook.getTemplateFile() != null) { + templateFileByte = FileUtils.readFileToByteArray(writeWorkbook.getTemplateFile()); + } else if (writeWorkbook.getTemplateInputStream() == null) { + try { + templateFileByte = IoUtils.toByteArray(writeWorkbook.getTemplateInputStream()); + } finally { + if (autoCloseStream) { + writeWorkbook.getTemplateInputStream().close(); + } + } + } + this.tempTemplateInputStream = new ByteArrayInputStream(templateFileByte); } public Workbook getWorkbook() { @@ -127,6 +166,14 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.workbook = workbook; } + public Workbook getCachedWorkbook() { + return cachedWorkbook; + } + + public void setCachedWorkbook(Workbook cachedWorkbook) { + this.cachedWorkbook = cachedWorkbook; + } + public Map getHasBeenInitializedSheet() { return hasBeenInitializedSheet; } @@ -167,6 +214,14 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.templateInputStream = templateInputStream; } + public InputStream getTempTemplateInputStream() { + return tempTemplateInputStream; + } + + public void setTempTemplateInputStream(InputStream tempTemplateInputStream) { + this.tempTemplateInputStream = tempTemplateInputStream; + } + public File getTemplateFile() { return templateFile; } @@ -199,12 +254,12 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.mandatoryUseInputStream = mandatoryUseInputStream; } - public Map getTemplateLastRowMap() { - return templateLastRowMap; + public String getPassword() { + return password; } - public void setTemplateLastRowMap(Map templateLastRowMap) { - this.templateLastRowMap = templateLastRowMap; + public void setPassword(String password) { + this.password = password; } @Override diff --git a/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java index 51b03d13..7aafc448 100644 --- a/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.style; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Workbook; @@ -27,28 +29,23 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She } @Override - public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } @Override - public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { - initCellStyle(writeWorkbookHolder.getWorkbook()); - hasInitialized = true; - } + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) { - @Override - public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { - if (!hasInitialized) { - initCellStyle(writeSheetHolder.getParentWriteWorkbookHolder().getWorkbook()); - hasInitialized = true; - } } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + if (isHead == null || head == null) { + return; + } if (isHead) { setHeadCellStyle(cell, head, relativeRowIndex); } else { @@ -56,6 +53,17 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She } } + @Override + public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + initCellStyle(writeWorkbookHolder.getWorkbook()); + hasInitialized = true; + } + /** * Initialization cell style * @@ -70,7 +78,7 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She * @param head * @param relativeRowIndex */ - protected abstract void setHeadCellStyle(Cell cell, Head head, int relativeRowIndex); + protected abstract void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex); /** * Sets the cell style of content @@ -79,6 +87,6 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She * @param head * @param relativeRowIndex */ - protected abstract void setContentCellStyle(Cell cell, Head head, int relativeRowIndex); + protected abstract void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex); } diff --git a/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java index 083d9cf7..09903a00 100644 --- a/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java @@ -29,7 +29,7 @@ public abstract class AbstractVerticalCellStyleStrategy extends AbstractCellStyl } @Override - protected void setHeadCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex) { int columnIndex = head.getColumnIndex(); if (headCellStyleCache.containsKey(columnIndex)) { CellStyle cellStyle = headCellStyleCache.get(columnIndex); @@ -49,7 +49,7 @@ public abstract class AbstractVerticalCellStyleStrategy extends AbstractCellStyl } @Override - protected void setContentCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) { int columnIndex = head.getColumnIndex(); if (contentCellStyleCache.containsKey(columnIndex)) { CellStyle cellStyle = contentCellStyleCache.get(columnIndex); diff --git a/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java index da5e53bc..295cd556 100644 --- a/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java @@ -51,7 +51,7 @@ public class HorizontalCellStyleStrategy extends AbstractCellStyleStrategy { } @Override - protected void setHeadCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex) { if (headCellStyle == null) { return; } @@ -59,7 +59,7 @@ public class HorizontalCellStyleStrategy extends AbstractCellStyleStrategy { } @Override - protected void setContentCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) { if (contentCellStyleList == null || contentCellStyleList.isEmpty()) { return; } diff --git a/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java index 67e9733a..07afcfc5 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.style.column; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; @@ -24,27 +26,28 @@ public abstract class AbstractColumnWidthStyleStrategy implements CellWriteHandl @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + 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 afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { - setColumnWidth(writeSheetHolder, cellData, cell, head, relativeRowIndex, isHead); + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + setColumnWidth(writeSheetHolder, cellDataList, cell, head, relativeRowIndex, isHead); } /** * Sets the column width when head create * * @param writeSheetHolder - * @param cellData + * @param cellDataList * @param cell * @param head * @param relativeRowIndex * @param isHead */ - protected abstract void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, - int relativeRowIndex, boolean isHead); + protected abstract void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java index da9b7dac..9908144f 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.style.column; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import com.alibaba.excel.metadata.CellData; @@ -12,10 +14,12 @@ import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; * @author Jiaju Zhuang */ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy { + @Override - protected void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, - int relativeRowIndex, boolean isHead) { - if (!isHead && relativeRowIndex != 0) { + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + boolean needSetWidth = relativeRowIndex != null && (isHead || relativeRowIndex == 0); + if (!needSetWidth) { return; } Integer width = columnWidth(head); @@ -36,4 +40,5 @@ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColum * @return */ protected abstract Integer columnWidth(Head head); + } diff --git a/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java index fb807f39..006927f9 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java @@ -1,12 +1,14 @@ package com.alibaba.excel.write.style.column; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.poi.ss.usermodel.Cell; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; /** @@ -24,9 +26,10 @@ public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthSty private static final Map> CACHE = new HashMap>(8); @Override - protected void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, - int relativeRowIndex, boolean isHead) { - if (!isHead && cellData == null) { + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList); + if (!needSetWidth) { return; } Map maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo()); @@ -34,24 +37,25 @@ public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthSty maxColumnWidthMap = new HashMap(16); CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap); } - Integer columnWidth = dataLength(cellData, cell, isHead); + Integer columnWidth = dataLength(cellDataList, cell, isHead); if (columnWidth < 0) { return; } if (columnWidth > MAX_COLUMN_WIDTH) { columnWidth = MAX_COLUMN_WIDTH; } - Integer maxColumnWidth = maxColumnWidthMap.get(head.getColumnIndex()); + Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex()); if (maxColumnWidth == null || columnWidth > maxColumnWidth) { - maxColumnWidthMap.put(head.getColumnIndex(), columnWidth); - writeSheetHolder.getSheet().setColumnWidth(head.getColumnIndex(), columnWidth * 256); + maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth); + writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256); } } - private Integer dataLength(CellData cellData, Cell cell, boolean isHead) { + private Integer dataLength(List cellDataList, Cell cell, Boolean isHead) { if (isHead) { return cell.getStringCellValue().getBytes().length; } + CellData cellData = cellDataList.get(0); switch (cellData.getType()) { case STRING: return cellData.getStringValue().getBytes().length; diff --git a/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java index 3a6a3431..303c8180 100644 --- a/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java @@ -20,14 +20,23 @@ public abstract class AbstractRowHeightStyleStrategy implements RowWriteHandler, } @Override - public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, int rowIndex, - int relativeRowIndex, boolean isHead) { + public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead) { } @Override public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - int relativeRowIndex, boolean isHead) { + Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + if (isHead == null) { + return; + } if (isHead) { setHeadColumnHeight(row, relativeRowIndex); } else { diff --git a/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java b/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java index 2a028f3f..f1e91197 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java @@ -29,4 +29,6 @@ public class AnnotationData { private Double number; @ExcelIgnore private String ignore; + private static final String staticFinal = "test"; + private transient String transientString; } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java index 5acd5db1..4489f1c1 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java @@ -48,6 +48,7 @@ public class AnnotationDataTest { data.setDate(DateUtils.parseDate("2020-01-01 01:01:01")); data.setNumber(99.99); data.setIgnore("忽略"); + data.setTransientString("忽略"); list.add(data); return list; } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataData.java b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataData.java new file mode 100644 index 00000000..a4e624fb --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataData.java @@ -0,0 +1,21 @@ +package com.alibaba.easyexcel.test.core.celldata; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.metadata.CellData; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +public class CellDataData { + @DateTimeFormat("yyyy年MM月dd日") + private CellData date; + private CellData integer1; + private Integer integer2; + private CellData formulaValue; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataListener.java new file mode 100644 index 00000000..3108f97a --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataListener.java @@ -0,0 +1,37 @@ +package com.alibaba.easyexcel.test.core.celldata; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +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 Jiaju Zhuang + */ +public class CellDataDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(CellDataDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(CellDataData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assert.assertEquals(list.size(), 1); + CellDataData cellDataData = list.get(0); + + Assert.assertEquals(cellDataData.getDate().getStringValue(), "2020年01月01日"); + Assert.assertEquals((long)cellDataData.getInteger1().getData(), 2L); + Assert.assertEquals((long)cellDataData.getInteger2(), 2L); + Assert.assertEquals(cellDataData.getFormulaValue().getFormulaValue(), "B2+C2"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataTest.java new file mode 100644 index 00000000..4615cdbc --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataTest.java @@ -0,0 +1,71 @@ +package com.alibaba.easyexcel.test.core.celldata; + +import java.io.File; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import com.alibaba.easyexcel.test.core.simple.SimpleData; +import com.alibaba.easyexcel.test.core.simple.SimpleDataListener; +import com.alibaba.easyexcel.test.core.simple.SimpleDataSheetNameListener; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.util.DateUtils; + +/** + * + * @author Jiaju Zhuang + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class CellDataDataTest { + + private static File file07; + private static File file03; + + @BeforeClass + public static void init() { + file07 = TestFileUtil.createNewFile("cellData07.xlsx"); + file03 = TestFileUtil.createNewFile("cellData03.xls"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03); + } + + private void readAndWrite(File file) throws Exception { + EasyExcel.write(file, CellDataData.class).sheet().doWrite(data()); + EasyExcel.read(file, CellDataData.class, new CellDataDataListener()).sheet().doRead(); + } + + private List data() throws Exception { + List list = new ArrayList(); + CellDataData cellDataData = new CellDataData(); + cellDataData.setDate(new CellData(DateUtils.parseDate("2020-01-01 01:01:01"))); + CellData integer1 = new CellData(); + integer1.setType(CellDataTypeEnum.NUMBER); + integer1.setNumberValue(BigDecimal.valueOf(2L)); + cellDataData.setInteger1(integer1); + cellDataData.setInteger2(2); + CellData formulaValue = new CellData(); + formulaValue.setFormula(Boolean.TRUE); + formulaValue.setFormulaValue("B2+C2"); + cellDataData.setFormulaValue(formulaValue); + list.add(cellDataData); + return list; + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptData.java b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptData.java new file mode 100644 index 00000000..19ef8d03 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptData.java @@ -0,0 +1,14 @@ +package com.alibaba.easyexcel.test.core.encrypt; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +public class EncryptData { + @ExcelProperty("姓名") + private String name; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataListener.java new file mode 100644 index 00000000..0fb603fb --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataListener.java @@ -0,0 +1,35 @@ +package com.alibaba.easyexcel.test.core.encrypt; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +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 Jiaju Zhuang + */ +public class EncryptDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(EncryptDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(EncryptData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assert.assertEquals(list.size(), 10); + Assert.assertEquals(list.get(0).getName(), "姓名0"); + Assert.assertEquals((int)(context.readSheetHolder().getSheetNo()), 0); + Assert.assertEquals( + context.readSheetHolder().getExcelReadHeadProperty().getHeadMap().get(0).getHeadNameList().get(0), "姓名"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} 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 new file mode 100644 index 00000000..98f580f8 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java @@ -0,0 +1,88 @@ +package com.alibaba.easyexcel.test.core.encrypt; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import com.alibaba.easyexcel.test.core.simple.SimpleData; +import com.alibaba.easyexcel.test.core.simple.SimpleDataListener; +import com.alibaba.easyexcel.test.core.simple.SimpleDataSheetNameListener; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.FileUtils; + +/** + * + * @author Jiaju Zhuang + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class EncryptDataTest { + + private static File file07; + private static File file03; + private static File file07OutputStream; + private static File file03OutputStream; + + @BeforeClass + public static void init() { + file07 = TestFileUtil.createNewFile("encrypt07.xlsx"); + file03 = TestFileUtil.createNewFile("encrypt03.xls"); + file07OutputStream = TestFileUtil.createNewFile("encryptOutputStream07.xlsx"); + file03OutputStream = TestFileUtil.createNewFile("encryptOutputStream03.xls"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t01ReadAndWriteStream07() throws Exception { + readAndWriteStream(file07OutputStream, ExcelTypeEnum.XLSX); + } + + @Test + public void t02ReadAndWriteStream03() throws Exception { + readAndWriteStream(file03OutputStream, ExcelTypeEnum.XLS); + } + + private void readAndWrite(File file) { + EasyExcel.write(file, EncryptData.class).password("123456").sheet().doWrite(data()); + EasyExcel.read(file, EncryptData.class, new EncryptDataListener()).password("123456").sheet().doRead(); + } + + private void readAndWriteStream(File file, ExcelTypeEnum excelType) throws Exception { + FileOutputStream fileOutputStream = new FileOutputStream(file); + EasyExcel.write(fileOutputStream, EncryptData.class).password("123456").excelType(excelType).sheet() + .doWrite(data()); + fileOutputStream.close(); + + FileInputStream fileInputStream = new FileInputStream(file); + EasyExcel.read(fileInputStream, EncryptData.class, new EncryptDataListener()).password("123456") + .excelType(excelType).sheet().doRead(); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + SimpleData simpleData = new SimpleData(); + simpleData.setName("姓名" + i); + list.add(simpleData); + } + return list; + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java index ffad8387..d9c26e26 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java @@ -20,7 +20,7 @@ public class ExceptionDataListener extends AnalysisEventListener @Override public void onException(Exception exception, AnalysisContext context) { - LOGGER.info("抛出异常,忽略:{}", exception.getMessage()); + LOGGER.info("抛出异常,忽略:{}", exception.getMessage(), exception); } @Override diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java new file mode 100644 index 00000000..13dfa17a --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.core.fill; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.converters.doubleconverter.DoubleStringConverter; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +public class FillData { + private String name; + @NumberFormat("#") + @ExcelProperty(converter = DoubleStringConverter.class) + private double number; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java new file mode 100644 index 00000000..93a5457a --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java @@ -0,0 +1,157 @@ +package com.alibaba.easyexcel.test.core.fill; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import com.alibaba.easyexcel.test.core.style.StyleData; +import com.alibaba.easyexcel.test.core.style.StyleDataListener; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.merge.LoopMergeStrategy; +import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy; +import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy; + +/** + * + * @author Jiaju Zhuang + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class FillDataTest { + + private static File file07; + private static File file03; + private static File simpleTemplate07; + private static File simpleTemplate03; + private static File fileComplex07; + private static File complexFillTemplate07; + private static File fileComplex03; + private static File complexFillTemplate03; + private static File fileHorizontal07; + private static File horizontalFillTemplate07; + private static File fileHorizontal03; + private static File horizontalFillTemplate03; + + @BeforeClass + public static void init() { + file07 = TestFileUtil.createNewFile("fill07.xlsx"); + file03 = TestFileUtil.createNewFile("fill03.xls"); + simpleTemplate07 = TestFileUtil.readFile("fill" + File.separator + "simple.xlsx"); + simpleTemplate03 = TestFileUtil.readFile("fill" + File.separator + "simple.xls"); + fileComplex07 = TestFileUtil.createNewFile("fillComplex07.xlsx"); + complexFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "complex.xlsx"); + fileComplex03 = TestFileUtil.createNewFile("fillComplex03.xls"); + complexFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "complex.xls"); + fileHorizontal07 = TestFileUtil.createNewFile("fillHorizontal07.xlsx"); + horizontalFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "horizontal.xlsx"); + fileHorizontal03 = TestFileUtil.createNewFile("fillHorizontal03.xls"); + horizontalFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "horizontal.xls"); + } + + @Test + public void t01Fill07() { + fill(file07, simpleTemplate07); + } + + @Test + public void t02Fill03() { + fill(file03, simpleTemplate03); + } + + @Test + public void t03ComplexFill07() { + complexFill(fileComplex07, complexFillTemplate07); + } + + @Test + public void t04ComplexFill03() { + complexFill(fileComplex03, complexFillTemplate03); + } + + @Test + public void t05HorizontalFill07() { + horizontalFill(fileHorizontal07, horizontalFillTemplate07); + } + + @Test + public void t06HorizontalFill03() { + horizontalFill(fileHorizontal03, horizontalFillTemplate03); + } + + private void horizontalFill(File file, File template) { + ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).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(); + + List list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync(); + Assert.assertEquals(list.size(), 5L); + Map map0 = (Map)list.get(0); + Assert.assertEquals("张三", map0.get(2)); + } + + private void complexFill(File file, File template) { + ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + 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(); + List list = EasyExcel.read(file).sheet().headRowNumber(3).doReadSync(); + Assert.assertEquals(list.size(), 21L); + Map map19 = (Map)list.get(19); + Assert.assertEquals("张三", map19.get(0)); + } + + private void fill(File file, File template) { + FillData fillData = new FillData(); + fillData.setName("张三"); + fillData.setNumber(5.2); + EasyExcel.write(file, FillData.class).withTemplate(template).sheet().doFill(fillData); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } + +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java index b7ba56b3..fa32433a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java @@ -1,13 +1,19 @@ package com.alibaba.easyexcel.test.core.large; import java.io.File; +import java.util.ArrayList; +import java.util.List; +import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.easyexcel.test.demo.write.DemoData; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.metadata.WriteSheet; /** * @@ -15,12 +21,67 @@ import com.alibaba.excel.EasyExcel; */ public class LargeDataTest { private static final Logger LOGGER = LoggerFactory.getLogger(LargeDataTest.class); + private static File fileFill07; + private static File template07; + private int i = 0; + + @BeforeClass + public static void init() { + fileFill07 = TestFileUtil.createNewFile("largefill07.xlsx"); + template07 = TestFileUtil.readFile("large" + File.separator + "fill.xlsx"); + } @Test - public void read() { + public void t01Read() { long start = System.currentTimeMillis(); EasyExcel.read(TestFileUtil.getPath() + "large" + File.separator + "large07.xlsx", LargeData.class, new LargeDataListener()).headRowNumber(2).sheet().doRead(); LOGGER.info("Large data total time spent:{}", System.currentTimeMillis() - start); } + + @Test + public void t02Fill() { + ExcelWriter excelWriter = EasyExcel.write(fileFill07).withTemplate(template07).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 100; j++) { + excelWriter.fill(data(), writeSheet); + LOGGER.info("{} fill success.", j); + } + excelWriter.finish(); + } + + private List data() { + List list = new ArrayList(); + int size = i + 5000; + for (; i < size; i++) { + LargeData largeData = new LargeData(); + list.add(largeData); + largeData.setStr1("str1-" + i); + largeData.setStr2("str2-" + i); + largeData.setStr3("str3-" + i); + largeData.setStr4("str4-" + i); + largeData.setStr5("str5-" + i); + largeData.setStr6("str6-" + i); + largeData.setStr7("str7-" + i); + largeData.setStr8("str8-" + i); + largeData.setStr9("str9-" + i); + largeData.setStr10("str10-" + i); + largeData.setStr11("str11-" + i); + largeData.setStr12("str12-" + i); + largeData.setStr13("str13-" + i); + largeData.setStr14("str14-" + i); + largeData.setStr15("str15-" + i); + largeData.setStr16("str16-" + i); + largeData.setStr17("str17-" + i); + largeData.setStr18("str18-" + i); + largeData.setStr19("str19-" + i); + largeData.setStr20("str20-" + i); + largeData.setStr21("str21-" + i); + largeData.setStr22("str22-" + i); + largeData.setStr23("str23-" + i); + largeData.setStr24("str24-" + i); + largeData.setStr25("str25-" + i); + } + return list; + } } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java index 579a821f..44e2f55b 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java @@ -40,6 +40,16 @@ public class MultipleSheetsDataTest { read(file03); } + @Test + public void t03Read07All() { + readAll(file07); + } + + @Test + public void t04Read03All() { + readAll(file03); + } + private void read(File file) { MultipleSheetsListener multipleSheetsListener = new MultipleSheetsListener(); ExcelReader excelReader = EasyExcel.read(file, MultipleSheetsData.class, multipleSheetsListener).build(); @@ -53,4 +63,8 @@ public class MultipleSheetsDataTest { excelReader.finish(); } + private void readAll(File file) { + EasyExcel.read(file, MultipleSheetsData.class, new MultipleSheetsListener()).doReadAll(); + } + } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java b/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java index 950f562b..f414be28 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java @@ -27,6 +27,7 @@ public class MultipleSheetsListener extends AnalysisEventListener map = new HashMap(); + map.put("name", "张三"); + map.put("number", 5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); + } + + /** + * 填充列表 + */ + @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(); + } + + /** + * 复杂的填充 + */ + @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(); + } + + /** + * 数据量大的复杂填充 + *

+ * 这里的解决方案是 确保模板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需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案 + } + + /** + * 横向的填充 + */ + @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(); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java new file mode 100644 index 00000000..896cea62 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java @@ -0,0 +1,50 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.fastjson.JSON; + +/** + * 读取头 + * + * @author Jiaju Zhuang + */ +public class CellDataDemoHeadDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(CellDataDemoHeadDataListener.class); + /** + * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + List list = new ArrayList(); + + @Override + public void invoke(CellDataReadDemoData data, AnalysisContext context) { + LOGGER.info("解析到一条数据:{}", JSON.toJSONString(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/CellDataReadDemoData.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataReadDemoData.java new file mode 100644 index 00000000..f5d01e0c --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataReadDemoData.java @@ -0,0 +1,22 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.Date; + +import com.alibaba.excel.metadata.CellData; + +import lombok.Data; + +/** + * 基础数据类.这里的排序和excel里面的排序一致 + * + * @author Jiaju Zhuang + **/ +@Data +public class CellDataReadDemoData { + private CellData string; + // 这里注意 虽然是日期 但是 类型 存储的是number 因为excel 存储的就是number + private CellData date; + private CellData doubleData; + // 这里并不一定能完美的获取 有些公式是依赖性的 可能会读不到 这个问题后续会修复 + private CellData formulaValue; +} 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 index bf4dd639..434af3b6 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java @@ -15,6 +15,7 @@ import com.alibaba.fastjson.JSON; * * @author Jiaju Zhuang */ +// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 public class DemoDataListener extends AnalysisEventListener { private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); /** diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java new file mode 100644 index 00000000..0f00719d --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java @@ -0,0 +1,80 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.fastjson.JSON; + +/** + * 读取转换异常 + * + * @author Jiaju Zhuang + */ +public class DemoExceptionListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(DemoExceptionListener.class); + /** + * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + List list = new ArrayList(); + + /** + * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。 + * + * @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()); + } + } + + /** + * 这里会一行行的返回头 + * + * @param headMap + * @param context + */ + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap)); + } + + @Override + public void invoke(ExceptionDemoData data, AnalysisContext context) { + LOGGER.info("解析到一条数据:{}", JSON.toJSONString(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/DemoHeadDataListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java index bc55052b..184409a4 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java @@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.exception.ExcelDataConvertException; import com.alibaba.fastjson.JSON; /** @@ -34,7 +35,12 @@ public class DemoHeadDataListener extends AnalysisEventListener { */ @Override public void onException(Exception exception, AnalysisContext context) { - LOGGER.error("解析失败,但是继续解析下一行", exception); + LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage()); + if (exception instanceof ExcelDataConvertException) { + ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception; + LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(), + excelDataConvertException.getColumnIndex()); + } } /** diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java new file mode 100644 index 00000000..8040728d --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.Date; + +import lombok.Data; + +/** + * 基础数据类.这里的排序和excel里面的排序一致 + * + * @author Jiaju Zhuang + **/ +@Data +public class ExceptionDemoData { + /** + * 用日期去接字符串 肯定报错 + */ + private Date date; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/NoModleDataListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/NoModleDataListener.java new file mode 100644 index 00000000..2e775387 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/NoModleDataListener.java @@ -0,0 +1,50 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +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; + +/** + * 直接用map接收数据 + * + * @author Jiaju Zhuang + */ +public class NoModleDataListener extends AnalysisEventListener> { + private static final Logger LOGGER = LoggerFactory.getLogger(NoModleDataListener.class); + /** + * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + List> list = new ArrayList>(); + + @Override + public void invoke(Map 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 index a8e6a61f..9ebafa4f 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java @@ -39,6 +39,7 @@ public class ReadTest { */ @Test public void simpleRead() { + // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 // 写法1: String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 @@ -71,7 +72,7 @@ public class ReadTest { } /** - * 读多个sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件 + * 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件 *

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

@@ -81,24 +82,21 @@ public class ReadTest { */ @Test public void repeatedRead() { - // 方法1 如果 sheet1 sheet2 都是同一数据 监听器和头 都写到最外层 String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build(); - ReadSheet readSheet1 = EasyExcel.readSheet(0).build(); - ReadSheet readSheet2 = EasyExcel.readSheet(1).build(); - excelReader.read(readSheet1); - excelReader.read(readSheet2); - // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 - excelReader.finish(); + // 读取全部sheet + // 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写 + EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll(); - // 方法2 如果 sheet1 sheet2 数据不一致的话 + // 读取部分sheet fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - excelReader = EasyExcel.read(fileName).build(); + ExcelReader excelReader = EasyExcel.read(fileName).build(); // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener - readSheet1 = EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - readSheet2 = EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - excelReader.read(readSheet1); - excelReader.read(readSheet2); + 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(); } @@ -117,7 +115,7 @@ public class ReadTest { @Test public void converterRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, ConverterData.class, new ConverterDataListener()) // 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。 // 如果就想单个字段使用请使用@ExcelProperty 指定converter @@ -140,7 +138,7 @@ public class ReadTest { @Test public void complexHeaderRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet() // 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,因为默认会根据DemoData 来解析,他没有指定头,也就是默认1行 .headRowNumber(1).doRead(); @@ -159,12 +157,12 @@ public class ReadTest { @Test public void headerRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); } /** - * 数据转换等异常处理 + * 读取公式和单元格类型 * *

* 1. 创建excel对应的实体对象 参照{@link DemoData} @@ -174,10 +172,27 @@ public class ReadTest { * 3. 直接读即可 */ @Test + public void cellDataRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "cellDataDemo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + EasyExcel.read(fileName, CellDataReadDemoData.class, new CellDataDemoHeadDataListener()).sheet().doRead(); + } + + /** + * 数据转换等异常处理 + * + *

+ * 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 然后千万别忘记 finish - EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + EasyExcel.read(fileName, ExceptionDemoData.class, new DemoExceptionListener()).sheet().doRead(); } /** @@ -202,4 +217,13 @@ public class ReadTest { } } + /** + * 不创建对象的读,不是特别推荐使用,都用String接收对日期的支持不是很好 + */ + @Test + public void noModleRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 只要,然后读取第一个sheet 同步读取会自动finish + EasyExcel.read(fileName, new NoModleDataListener()).sheet().doRead(); + } } diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java index 8fff02ca..cc525629 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java @@ -36,7 +36,7 @@ public class WebTest { */ @GetMapping("download") public void download(HttpServletResponse response) throws IOException { - // 这里注意 有同学反应下载的文件名不对。这个时候 请别使用swagger 他会有影响 + // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java index 4c5d0c6b..44ce5cc7 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java @@ -13,8 +13,6 @@ import lombok.Data; **/ @Data public class ComplexHeadData { - private final static String TITLE = "title"; - private transient String trans = "transient"; @ExcelProperty({"主标题", "字符串标题"}) private String string; @ExcelProperty({"主标题", "日期标题"}) diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java index bff544a1..b92d44a2 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java @@ -1,5 +1,7 @@ package com.alibaba.easyexcel.test.demo.write; +import java.util.List; + import org.apache.poi.common.usermodel.HyperlinkType; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CreationHelper; @@ -25,13 +27,19 @@ public class CustomCellWriteHandler implements CellWriteHandler { @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + 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 afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + 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) { 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 index b65fbf04..8a7902b5 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java @@ -2,6 +2,8 @@ package com.alibaba.easyexcel.test.demo.write; import java.util.Date; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; @@ -19,4 +21,9 @@ public class DemoData { private Date date; @ExcelProperty("数字标题") private Double doubleData; + /** + * 忽略这个字段 + */ + @ExcelIgnore + private String ignore; } 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 index 33420edf..2582f105 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -85,7 +85,7 @@ public class WriteTest { *

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

- * 3. 直接写即可,写入时会忽略static final和 transient + * 3. 直接写即可 */ @Test public void complexHeadWrite() { @@ -378,6 +378,17 @@ public class WriteTest { .registerWriteHandler(new CustomCellWriteHandler()).sheet("模板").doWrite(data()); } + /** + * 不创建对象的写 + */ + @Test + public void noModleWrite() { + // 写法1 + String fileName = TestFileUtil.getPath() + "noModleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName).head(head()).sheet("模板").doWrite(dataList()); + } + private List dataLong() { List list = new ArrayList(); for (int i = 0; i < 10; i++) { @@ -404,6 +415,18 @@ public class WriteTest { return list; } + private List> dataList() { + List> list = new ArrayList>(); + for (int i = 0; i < 10; i++) { + List data = new ArrayList(); + data.add("字符串" + i); + data.add(new Date()); + data.add(0.56); + list.add(data); + } + return list; + } + private List data() { List list = new ArrayList(); for (int i = 0; i < 10; i++) { 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/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java index 733f1a2f..fb39de15 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java @@ -3,6 +3,7 @@ package com.alibaba.easyexcel.test.temp; import java.io.File; import java.io.FileInputStream; import java.util.List; +import java.util.Map; import org.junit.Ignore; import org.junit.Test; @@ -25,7 +26,7 @@ public class LockTest { public void test() throws Exception { List list = - EasyExcel.read(new FileInputStream("D:\\test\\t222.xlsx")).sheet().headRowNumber(0).doReadSync(); + EasyExcel.read(new FileInputStream("D:\\test\\testbug嘉惠.xlsx")).sheet().headRowNumber(0).doReadSync(); for (Object data : list) { LOGGER.info("返回数据:{}", JSON.toJSONString(data)); } @@ -35,4 +36,14 @@ public class LockTest { } } + @Test + public void test2() throws Exception { + List list = + EasyExcel.read(new FileInputStream("D:\\test\\null.xlsx")).sheet().headRowNumber(0).doReadSync(); + for (Object data : list) { + LOGGER.info("返回数据:{}", ((Map)data).size()); + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java b/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java index 1424c426..43726ad3 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java @@ -1,11 +1,13 @@ package com.alibaba.easyexcel.test.temp.large; +import lombok.Builder; import lombok.Data; /** * @author Jiaju Zhuang */ @Data +@Builder public class LargeData { private String str1; diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi3Test.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi3Test.java new file mode 100644 index 00000000..2ff3427d --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi3Test.java @@ -0,0 +1,73 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.crypt.EncryptionInfo; +import org.apache.poi.poifs.crypt.EncryptionMode; +import org.apache.poi.poifs.crypt.Encryptor; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellCopyPolicy; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.Ignore; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.util.FileUtils; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ +@Ignore +public class Poi3Test { + private static final Logger LOGGER = LoggerFactory.getLogger(Poi3Test.class); + + @Test + public void Encryption() throws Exception { + String file = TestFileUtil.getPath() + "large" + File.separator + "large07.xlsx"; + POIFSFileSystem fs = new POIFSFileSystem(); + EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile); + Encryptor enc = info.getEncryptor(); + enc.confirmPassword("foobaa"); + OPCPackage opc = OPCPackage.open(new File(file), PackageAccess.READ_WRITE); + OutputStream os = enc.getDataStream(fs); + opc.save(os); + opc.close(); + + // Write out the encrypted version + FileOutputStream fos = new FileOutputStream("D:\\test\\99999999999.xlsx"); + fs.writeFilesystem(fos); + fos.close(); + fs.close(); + + } + + @Test + public void Encryption2() throws Exception { + Biff8EncryptionKey.setCurrentUserPassword("123456"); + POIFSFileSystem fs = new POIFSFileSystem(new File("d:/test/simple03.xls"), true); + HSSFWorkbook hwb = new HSSFWorkbook(fs.getRoot(), true); + Biff8EncryptionKey.setCurrentUserPassword(null); + System.out.println(hwb.getSheetAt(0).getSheetName()); + + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java index 9113960e..ee7be1e8 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java @@ -1,9 +1,15 @@ package com.alibaba.easyexcel.test.temp.poi; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; -import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellCopyPolicy; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFRow; import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; @@ -17,6 +23,8 @@ import org.slf4j.LoggerFactory; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.util.FileUtils; /** * 测试poi @@ -51,4 +59,169 @@ public class PoiTest { xssfSheet.createRow(20); LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); } + + @Test + public void lastRowNum233() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + Workbook xx = new XSSFWorkbook(file); + System.out.println(new File(file).exists()); + + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + + Cell cell = xssfSheet.getRow(0).createCell(9); + cell.setCellValue("testssdf是士大夫否t"); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + xssfWorkbook.write(fileout); + xssfWorkbook.close(); + } + + @Test + public void lastRowNum255() throws IOException, InvalidFormatException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "complex.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + xssfSheet.shiftRows(2, 4, 10); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void cp() throws IOException, InvalidFormatException { + String file = "d://test/tt.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + XSSFRow row = xssfSheet.getRow(2); + xssfSheet.removeRow(row); +// Row r2= xssfSheet.createRow(2); +// r2.createCell(1); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + + + SXSSFSheet sxssfSheet = sxssfWorkbook.getSheetAt(0); + sxssfSheet.createRow(2); + + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void lastRowNum233443() throws IOException, InvalidFormatException { + String file = "d://test/tt.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + XSSFRow row = xssfSheet.getRow(2); + xssfSheet.removeRow(row); + new CellCopyPolicy().createBuilder().build(); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void lastRowNum2333() throws IOException, InvalidFormatException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + Cell cell = xssfSheet.getRow(0).createCell(9); + cell.setCellValue("testssdf是士大夫否t"); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void testread() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + // + // Cell cell = xssfSheet.getRow(0).createCell(9); + + String file1 = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + + SXSSFWorkbook xssfWorkbook1 = new SXSSFWorkbook(new XSSFWorkbook(file1)); + Sheet xssfSheet1 = xssfWorkbook1.getXSSFWorkbook().getSheetAt(0); + + // Cell cell1 = xssfSheet1.getRow(0).createCell(9); + + xssfWorkbook.close(); + xssfWorkbook1.close(); + } + + @Test + public void testreadRead() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + FileUtils.readFileToByteArray(new File(file)); + } + + @Test + public void lastRowNum2332222() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + + Cell cell = xssfSheet.getRow(0).createCell(9); + cell.setCellValue("testssdf是士大夫否t"); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + xssfWorkbook.write(fileout); + } + + @Test + public void lastRowNum23443() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + xssfWorkbook.write(fileout); + xssfWorkbook.close(); + } + + @Test + public void lastRowNum2() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getPhysicalNumberOfRows()); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + LOGGER.info("一共行数:{}", xssfSheet.getFirstRowNum()); + + } + + @Test + public void lastRowNumXSSF2() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + LOGGER.info("一共:{}个sheet", xssfWorkbook.getNumberOfSheets()); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + XSSFRow row = xssfSheet.getRow(0); + LOGGER.info("第一行数据:{}", row); + } + } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java new file mode 100644 index 00000000..3a70d0d4 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java @@ -0,0 +1,156 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.regex.Pattern; + +import org.apache.poi.ss.formula.functions.T; +import org.apache.poi.xssf.streaming.SXSSFCell; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.junit.Ignore; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.metadata.CellData; +import com.alibaba.fastjson.JSON; + +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ +@Ignore +public class PoiWriteTest { + private static final Logger LOGGER = LoggerFactory.getLogger(PoiWriteTest.class); + + @Test + public void write0() throws IOException { + FileOutputStream fileOutputStream = + new FileOutputStream("D://test//tt132" + System.currentTimeMillis() + ".xlsx"); + SXSSFWorkbook sxxsFWorkbook = new SXSSFWorkbook(); + SXSSFSheet sheet = sxxsFWorkbook.createSheet("t1"); + SXSSFRow row = sheet.createRow(0); + SXSSFCell cell1 = row.createCell(0); + cell1.setCellValue(999999999999999L); + SXSSFCell cell2 = row.createCell(1); + cell2.setCellValue(1000000000000001L); + sxxsFWorkbook.write(fileOutputStream); + } + + @Test + public void write() throws IOException { + FileOutputStream fileOutputStream = + new FileOutputStream("D://test//tt132" + System.currentTimeMillis() + ".xlsx"); + SXSSFWorkbook sxxsFWorkbook = new SXSSFWorkbook(); + SXSSFSheet sheet = sxxsFWorkbook.createSheet("t1"); + SXSSFRow row = sheet.createRow(0); + SXSSFCell cell1 = row.createCell(0); + cell1.setCellValue(Long.toString(999999999999999L)); + SXSSFCell cell2 = row.createCell(1); + cell2.setCellValue(Long.toString(1000000000000001L)); + sxxsFWorkbook.write(fileOutputStream); + } + + @Test + public void write1() throws IOException { + System.out.println(JSON.toJSONString(long2Bytes(-999999999999999L))); + System.out.println(JSON.toJSONString(long2Bytes(-9999999999999999L))); + } + + public static byte[] long2Bytes(long num) { + byte[] byteNum = new byte[8]; + for (int ix = 0; ix < 8; ++ix) { + int offset = 64 - (ix + 1) * 8; + byteNum[ix] = (byte)((num >> offset) & 0xff); + } + return byteNum; + } + + private static final Pattern FILL_PATTERN = Pattern.compile("^.*?\\$\\{[^}]+}.*?$"); + + @Test + public void part() throws IOException { + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${number}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}今年").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("今年${number岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("胜多负少").matches()); + } + + private static final Pattern FILL_PATTERN2 = Pattern.compile("测试"); + + @Test + public void part2() throws IOException { + LOGGER.info("test:{}", FILL_PATTERN.matcher("我是测试呀").find()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("测试u").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("我是测试").matches()); + + } + + @Test + public void test() throws Exception { + Class clazz = TestCell.class; + + Field field = clazz.getDeclaredField("c2"); + // 通过getDeclaredField可以获得成员变量,但是对于Map来说,仅仅可以知道它是个Map,无法知道键值对各自的数据类型 + + Type gType = field.getGenericType(); + // 获得field的泛型类型 + + // 如果gType是ParameterizedType对象(参数化) + if (gType instanceof ParameterizedType) { + + ParameterizedType pType = (ParameterizedType)gType; + // 就把它转换成ParameterizedType对象 + + Type[] tArgs = pType.getActualTypeArguments(); + // 获得泛型类型的泛型参数(实际类型参数) + ParameterizedTypeImpl c = (ParameterizedTypeImpl)pType.getActualTypeArguments()[0]; + Class ttt = c.getRawType(); + System.out.println(ttt); + } else { + System.out.println("出错!!!"); + } + + } + + @Test + public void test2() throws Exception { + Class clazz = TestCell.class; + + Field field = clazz.getDeclaredField("c2"); + // 通过getDeclaredField可以获得成员变量,但是对于Map来说,仅仅可以知道它是个Map,无法知道键值对各自的数据类型 + + Type gType = field.getGenericType(); + // 获得field的泛型类型 + + // 如果gType是ParameterizedType对象(参数化) + if (gType instanceof ParameterizedType) { + + ParameterizedType pType = (ParameterizedType)gType; + // 就把它转换成ParameterizedType对象 + + Type[] tArgs = pType.getActualTypeArguments(); + // 获得泛型类型的泛型参数(实际类型参数) + ParameterizedTypeImpl c = (ParameterizedTypeImpl)pType.getActualTypeArguments()[0]; + Class ttt = c.getRawType(); + System.out.println(ttt); + } else { + System.out.println("出错!!!"); + } + + } + +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/TestCell.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/TestCell.java new file mode 100644 index 00000000..3c4e0ab6 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/TestCell.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.util.List; + +import com.alibaba.excel.metadata.CellData; + +import lombok.Data; + +/** + * TODO + * + * @author 罗成 + **/ +@Data +public class TestCell { + private CellData c1; + private CellData> c2; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java index 9b0d5402..2eb4d1ab 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java @@ -2,6 +2,8 @@ package com.alibaba.easyexcel.test.temp.simple; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.URL; import java.util.List; import org.junit.Ignore; @@ -10,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.cache.MapCache; import com.alibaba.fastjson.JSON; /** @@ -24,7 +27,18 @@ public class HgTest { @Test public void hh() throws IOException { List list = - EasyExcel.read(new FileInputStream("D:\\test\\testempty.xls")).headRowNumber(0).sheet().doReadSync(); + EasyExcel.read(new FileInputStream("D:\\test\\201909301017rule.xlsx")).headRowNumber(2).sheet().doReadSync(); + for (Object data : list) { + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void hh5() throws IOException { + URL url = new URL("http://hotelcontractfil.oss-cn-beijing.aliyuncs.com/2019/%E5%98%89%E6%83%A0-%E4%B8%AD%E4%BA%A4%E5%BB%BA_2019-09-01_2019-09-30_1569055677522.xlsx?Expires=1884415681&OSSAccessKeyId=LTAIGZDkqZfPArBr&Signature=Rf0gbO8vl3l%2Brj1KdyzHHMsUhCE%3D"); + InputStream is = url.openStream(); + List list = + EasyExcel.read(is).headRowNumber(0).sheet().doReadSync(); for (Object data : list) { LOGGER.info("返回数据:{}", JSON.toJSONString(data)); } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java index d5a39d6a..27bea33c 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java @@ -33,7 +33,7 @@ public class Wirte { String fileName = TestFileUtil.getPath() + "t22" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入excelType参数即可 - EasyExcel.write(fileName).sheet("模板").head(head()).doWrite(data()); + EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data()); } private List> head() { diff --git a/src/test/resources/demo/cellDataDemo.xlsx b/src/test/resources/demo/cellDataDemo.xlsx new file mode 100644 index 00000000..947229c7 Binary files /dev/null and b/src/test/resources/demo/cellDataDemo.xlsx differ diff --git a/src/test/resources/demo/fill/complex.xlsx b/src/test/resources/demo/fill/complex.xlsx new file mode 100644 index 00000000..53767136 Binary files /dev/null and b/src/test/resources/demo/fill/complex.xlsx differ diff --git a/src/test/resources/demo/fill/complexFillWithTable.xlsx b/src/test/resources/demo/fill/complexFillWithTable.xlsx new file mode 100644 index 00000000..4de1a1e7 Binary files /dev/null and b/src/test/resources/demo/fill/complexFillWithTable.xlsx differ diff --git a/src/test/resources/demo/fill/horizontal.xlsx b/src/test/resources/demo/fill/horizontal.xlsx new file mode 100644 index 00000000..c8b4564e Binary files /dev/null and b/src/test/resources/demo/fill/horizontal.xlsx differ diff --git a/src/test/resources/demo/fill/list.xlsx b/src/test/resources/demo/fill/list.xlsx new file mode 100644 index 00000000..717cb498 Binary files /dev/null and b/src/test/resources/demo/fill/list.xlsx differ diff --git a/src/test/resources/demo/fill/simple.xlsx b/src/test/resources/demo/fill/simple.xlsx new file mode 100644 index 00000000..7514d1da Binary files /dev/null and b/src/test/resources/demo/fill/simple.xlsx differ diff --git a/src/test/resources/fill/complex.xls b/src/test/resources/fill/complex.xls new file mode 100644 index 00000000..d5758951 Binary files /dev/null and b/src/test/resources/fill/complex.xls differ diff --git a/src/test/resources/fill/complex.xlsx b/src/test/resources/fill/complex.xlsx new file mode 100644 index 00000000..53767136 Binary files /dev/null and b/src/test/resources/fill/complex.xlsx differ diff --git a/src/test/resources/fill/horizontal.xls b/src/test/resources/fill/horizontal.xls new file mode 100644 index 00000000..570f9012 Binary files /dev/null and b/src/test/resources/fill/horizontal.xls differ diff --git a/src/test/resources/fill/horizontal.xlsx b/src/test/resources/fill/horizontal.xlsx new file mode 100644 index 00000000..c8b4564e Binary files /dev/null and b/src/test/resources/fill/horizontal.xlsx differ diff --git a/src/test/resources/fill/simple.xls b/src/test/resources/fill/simple.xls new file mode 100644 index 00000000..ea66e944 Binary files /dev/null and b/src/test/resources/fill/simple.xls differ diff --git a/src/test/resources/fill/simple.xlsx b/src/test/resources/fill/simple.xlsx new file mode 100644 index 00000000..be1753cb Binary files /dev/null and b/src/test/resources/fill/simple.xlsx differ diff --git a/src/test/resources/large/fill.xlsx b/src/test/resources/large/fill.xlsx new file mode 100644 index 00000000..c3c376d3 Binary files /dev/null and b/src/test/resources/large/fill.xlsx differ diff --git a/update.md b/update.md index 769f0955..2ae7d28f 100644 --- a/update.md +++ b/update.md @@ -1,3 +1,15 @@ +# 2.1.0-beta1 +* 新增支持导入、导出支持公式 +* 新增支持读取单元格类型、写入指定单元格类型 +* 支持通过模板填充数据 +* 新增写支持 禁用头样式 `useDefaultStyle` +* 用map读取数据 空的单元格也会有个 null的数据 +* 转换报错 能获取到对应的行号和列号 +* 优化读取全部sheet方案 +* 新增注解`ExcelIgnoreUnannotated` 支持忽略未加`ExcelProperty`注解的字段 +* 支持导出加密 [Issue #361](https://github.com/alibaba/easyexcel/issues/361) +* 支持导入加密 [Issue #295](https://github.com/alibaba/easyexcel/issues/295) + # 2.0.5 * 优化07版超大文件读取方案 * 支持自己设置超大文件读取参数 @@ -53,23 +65,33 @@ * sheetNo 改成0开始 * 读支持指定列名 * 升级poi 到4.0.1 + # 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