├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── github
│ └── Dorae132
│ └── easyutil
│ └── easyexcel
│ ├── ExcelProperties.java
│ ├── ExcelUtils.java
│ ├── common
│ ├── EasyExcelException.java
│ └── Pair.java
│ ├── export
│ ├── DefaultWorkbookProcessor.java
│ ├── ExcelCol.java
│ ├── FillSheetModeEnums.java
│ ├── IDataSupplier.java
│ ├── IFillSheet.java
│ └── IWorkbookProcessor.java
│ └── read
│ ├── ConsumeRowThread.java
│ ├── DefaultReadDoneCallBackProcessor.java
│ ├── ExcelVersionEnums.java
│ ├── IReadDoneCallBack.java
│ ├── IReadDoneCallBackProcessor.java
│ ├── IRowConsumer.java
│ └── event
│ ├── IHandlerContext.java
│ ├── excel03
│ ├── Default03RecordHandlerContext.java
│ ├── IRecordHandlerContext.java
│ ├── XlsListener.java
│ └── handler
│ │ ├── Abstract03RecordHandler.java
│ │ ├── BlankRecordHandler.java
│ │ ├── BoundSheetRecordHandler.java
│ │ ├── EofRecordHandler.java
│ │ ├── NumberRecordHandler.java
│ │ ├── RowEndRecordHandler.java
│ │ ├── SSTRecordHandler.java
│ │ ├── StringRecordHandler.java
│ │ └── TailRecordHandler.java
│ └── excel07
│ ├── Default07RecordHandlerContext.java
│ └── XlsxHandler.java
└── test
└── java
└── com
└── github
└── Dorae132
└── easyutil
└── easyexcel
└── UtilsTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | .settings/
3 | .classpath
4 | .project
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 你的ExcelUtil简单、高效、易扩展吗
2 | > Author: Dorae
3 | > Date: 2018年10月23日12:30:15
4 | > 转载请注明出处
5 |
6 | ----
7 |
8 | ## 一、背景
9 |
10 | 最近在看和Excel导出相关的代码,但是:
11 |
12 | 1. 大部分项目中的excelutil工具类会存在安全问题,而且无法扩展;
13 | 2. 一些开源的Excel工具太过重量级,并且不不能完全适合业务定制;
14 | 3. 于是乎产生了**easyExcel**(巧合,和阿里的easeExcel重名了😀)。
15 |
16 | #### HSS、XSS
17 |
18 | 如果数据量较小的话,这种方式并不会存在问题,但是当数据量比较大时,存在非常严重的问题:
19 |
20 | 1. OOM(因为数据并没有被即使的写入磁盘);
21 | 2. 耗时(串行化);
22 | 3. HSS对单sheet的数据量限制(65535),XSS为100万+。
23 |
24 |
25 | HSSFWorkbook workbook = new HSSFWorkbook();
26 | HSSFSheet sheet = workbook.createSheet(sheetName);
27 | // 创建表头
28 | ...
29 | // 写入数据
30 | for (...) {
31 | HSSFRow textRow = sheet.createRow(rowIndex);
32 | for (...) {
33 | HSSFCell textcell = textRow.createCell(colIndex);
34 | textcell.setCellValue(value);
35 | }
36 | }
37 |
38 | #### SXSS
39 |
40 | 为了改善上出方案的问题,SXSS采用了缓冲的方式,即按照某种策略定期刷盘。从而解决了OOM与耗时太长的问题。使用姿势:
41 |
42 | InputStream inputStream = new FileInputStream(file);
43 | XSSFWorkbook xssfWorkbook = new XSSFWorkbook(inputStream);
44 | SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook,properties.getRowAccessWindowsize());
45 | Sheet sheet = sxssfWorkbook.getSheet(properties.getSheetName());
46 | if (sheet == null) {
47 | sheet = sxssfWorkbook.createSheet(properties.getSheetName());
48 | }
49 | // 创建表头
50 | ...
51 | // 写入数据
52 | for (...) {
53 | Row row = sheet.createRow(row);
54 | row.setValue(value);
55 | }
56 | FileOutputStream outputStream = new FileOutputStream(file);
57 | sxssfWorkbook.write(outputStream);
58 |
59 | ## 二、EasyUtil
60 |
61 | 虽然SXSS解决了OMM与耗时的问题,但是使用起来不太方便,会造成很多重复代码,于是产生了**EasyUtil**,在介绍之前我们先思考几个问题:
62 |
63 | 1. excel操作有哪些公用逻辑可以抽离;
64 | 2. 业务需求是什么,有哪些逻辑可以抽离;
65 | 3. 如何开放较好的扩展点。
66 |
67 | #### 架构
68 |
69 | 按照上述的几个问题,EasyUtil-1.0的架构如图1-1所示,其中绿色部分为扩展点。
70 |
71 | 1. IFillSheet为填充excel的接口,可以扩展自定义格式,目前在FillSheetModeEnums中提供了两种(建议使用APPENDMODE);
72 | 2. AbstractDataSupplier为需要根据业务实现的数据获取接口,类似于stream;
73 | 3. IExcelProcesor生成的file的后处理接口,比如可以将文件上传到某个公共空间。
74 |
75 |
76 |
77 |
78 |
79 | 图 1-1
80 |
81 |
82 | #### 使用姿势
83 |
84 | ##### git
85 |
86 | [戳这里](https://github.com/Dorae132/easyexcel)
87 |
88 | ##### pom
89 |
90 | com.github.Dorae132
91 | easyutil.easyexcel
92 | 1.2.0
93 |
94 | ##### test
95 |
96 | static class TestValue {
97 | @ExcelCol(title = "姓名")
98 | private String name;
99 | @ExcelCol(title = "年龄", order = 1)
100 | private String age;
101 | @ExcelCol(title = "学校", order = 3)
102 | private String school;
103 | @ExcelCol(title = "年级", order = 2)
104 | private String clazz;
105 |
106 | public TestValue(String name, String age, String school, String clazz) {
107 | super();
108 | this.name = name;
109 | this.age = age;
110 | this.school = school;
111 | this.clazz = clazz;
112 | }
113 | }
114 |
115 | private List getData(int count) {
116 | List dataList = Lists.newArrayListWithCapacity(10000);
117 | for (int i = 0; i < count; i++) {
118 | dataList.add(new TestValue("张三" + i, "age: " + i, null, "clazz: " + i));
119 | }
120 | return dataList;
121 | }
122 |
123 | @Test
124 | public void testAppend() throws Exception {
125 | List dataList = getData(10000);
126 | long start = System.currentTimeMillis();
127 | ExcelProperties properties = ExcelProperties.produceAppendProperties("",
128 | "C:\\Users\\Dorae\\Desktop\\ttt\\", "append.xlsx", 0, TestValue.class, 0, null, new AbstractDataSupplier() {
129 | private int i = 0;
130 |
131 | @Override
132 | public Pair, Boolean> getDatas() {
133 | boolean hasNext = i < 10;
134 | i++;
135 | return Pair.of(dataList, hasNext);
136 | }
137 | });
138 | File file = (File) ExcelUtils.excelExport(properties, FillSheetModeEnums.APPEND_MODE.getValue());
139 | System.out.println("apendMode: " + (System.currentTimeMillis() - start));
140 | }
141 |
142 | #### 效果
143 |
144 | 1. 10万行4列数据耗时3s左右(我的渣渣笔记本);
145 | 2. 对内存基本没消耗(当然需要合适的缓冲参数)。
146 |
147 | ## 三、To do
148 |
149 | 1. 导出excel和获取数据的接口并行化;(done, see the PARALLEL_APPEND_MODE)
150 | 2. 完善utils。
151 |
152 |
153 |
154 | # 读取篇
155 |
156 | # 高效读取百万级数据
157 |
158 | 接[上一篇](https://github.com/Dorae132/easyexcel/blob/master/README.md)介绍的高效写文件之后,最近抽时间研究了下Excel文件的读取。概括来讲,poi读取excel有两种方式:**用户模式和事件模式**。
159 |
160 | 然而很多业务场景中的读取Excel仍然采用用户模式,但是这种模式需要创建大量对象,对大文件的支持非常不友好,非常容易OOM。但是对于事件模式而言,往往需要自己实现listener,并且需要根据自己需要解析不同的event,所以用起来比较复杂。
161 |
162 | 基于此,EasyExcel封装了常用的Excel格式文档的事件解析,并且提供了接口供开发小哥**扩展定制化**,实现让你解析Excel不再费神的目的。
163 |
164 | Talk is cheap, show me the code.
165 |
166 | ### 使用姿势
167 |
168 |
169 | #### 普通姿势
170 |
171 | 看看下边的姿势,是不是觉得**只需要关心业务逻辑了**?
172 |
173 | ExcelUtils.excelRead(ExcelProperties.produceReadProperties("C:\\Users\\Dorae\\Desktop\\ttt\\",
174 | "append_0745704108fa42ffb656aef983229955.xlsx"), new IRowConsumer() {
175 | @Override
176 | public void consume(List row) {
177 | System.out.println(row);
178 | count.incrementAndGet();
179 | try {
180 | TimeUnit.MICROSECONDS.sleep(100);
181 | } catch (InterruptedException e) {
182 | // TODO Auto-generated catch block
183 | e.printStackTrace();
184 | }
185 | }
186 | }, new IReadDoneCallBack() {
187 | @Override
188 | public Void call() {
189 | System.out.println(
190 | "end, count: " + count.get() + "\ntime: " + (System.currentTimeMillis() - start));
191 | return null;
192 | }
193 | }, 3, true);
194 |
195 |
196 | #### 定制姿势
197 |
198 | 什么?你想**定制context,添加handler**?请看下边!你只需要实现一个Abstract03RecordHandler然后regist到context(关注ExcelVersionEnums中的factory)就可以了。
199 |
200 | public static void excelRead(IHandlerContext context, IRowConsumer rowConsumer, IReadDoneCallBack callBack,
201 | int threadCount, boolean syncCurrentThread) throws Exception {
202 | // synchronized main thread
203 | CyclicBarrier cyclicBarrier = null;
204 | threadCount = syncCurrentThread ? ++threadCount : threadCount;
205 | if (callBack != null) {
206 | cyclicBarrier = new CyclicBarrier(threadCount, () -> {
207 | callBack.call();
208 | });
209 | } else {
210 | cyclicBarrier = new CyclicBarrier(threadCount);
211 | }
212 | for (int i = 0; i < threadCount; i++) {
213 | THREADPOOL.execute(new ConsumeRowThread(context, rowConsumer, cyclicBarrier));
214 | }
215 | context.process();
216 | if (syncCurrentThread) {
217 | cyclicBarrier.await();
218 | }
219 | }
220 |
221 | ### 框架结构
222 |
223 | 如图,为整个EasyExcel的结构,其中(如果了解过设计模式,或者读过相关源码,应该会很容易理解):
224 |
225 | 1. 绿色为可扩展接口,
226 | 2. 上半部分为写文件部分,下办部分为读文件。
227 |
228 | 
229 |
230 | ### 总结
231 |
232 | 至此,EasyExcel的基本功能算是完善了,欢迎各路大神提Issue过来。🍗
233 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.github.Dorae132
7 | easyutil.easyexcel
8 | 1.2.0
9 | jar
10 |
11 | easyexcel
12 | excel utils
13 |
14 |
15 |
16 | dorae132
17 | https://github.com/Dorae132/easyexcel
18 |
19 |
20 |
21 |
22 | The Apache Software License, Version 2.0
23 | http://www.apache.org/licenses/LICENSE-2.0.txt
24 |
25 |
26 |
27 | scm:git:https://github.com/Dorae132/easyexcel.git
28 | scm:git:https://github.com/Dorae132/easyexcel.git
29 | https://github.com/Dorae132/easyexcel
30 |
31 | https://github.com/Dorae132/easyexcel
32 |
33 | UTF-8
34 | UTF-8
35 | 1.8
36 | 1.5.2.RELEASE
37 | 1.8
38 | 1.8
39 | 1.8
40 | 3.17
41 |
42 |
43 |
44 |
45 |
46 | org.apache.poi
47 | poi
48 | ${poi.version}
49 |
50 |
51 | org.apache.poi
52 | poi-ooxml
53 | ${poi.version}
54 |
55 |
56 | org.apache.poi
57 | poi-ooxml-schemas
58 | ${poi.version}
59 |
60 |
61 | org.bluestemsoftware.open.maven.tparty
62 | xerces-impl
63 | 2.9.0
64 |
65 |
66 | xml-apis
67 | xml-apis
68 | 2.0.2
69 |
70 |
71 | org.apache.xmlbeans
72 | xmlbeans
73 | 2.6.0
74 |
75 |
76 | cglib
77 | cglib
78 | 2.2.2
79 |
80 |
81 | commons-collections
82 | commons-collections
83 | 3.2.1
84 |
85 |
86 |
87 | com.google.guava
88 | guava
89 | 18.0
90 |
91 |
92 |
93 | org.apache.commons
94 | commons-lang3
95 | 3.6
96 |
97 |
98 |
99 | junit
100 | junit
101 | 4.9
102 | test
103 |
104 |
105 |
106 |
107 |
108 | release
109 |
110 |
111 |
112 |
113 | org.apache.maven.plugins
114 | maven-source-plugin
115 | 2.2.1
116 |
117 |
118 | package
119 |
120 | jar-no-fork
121 |
122 |
123 |
124 |
125 |
126 |
127 | org.apache.maven.plugins
128 | maven-javadoc-plugin
129 | 2.9.1
130 |
131 | private
132 | true
133 | UTF-8
134 | UTF-8
135 | UTF-8
136 | -Xdoclint:none
137 |
138 |
139 |
140 | package
141 |
142 | jar
143 |
144 |
145 |
146 |
147 |
148 |
149 | org.apache.maven.plugins
150 | maven-gpg-plugin
151 | 1.5
152 |
153 |
154 | verify
155 |
156 | sign
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | dorae_maven_central
166 | https://oss.sonatype.org/content/repositories/snapshots/
167 |
168 |
169 | dorae_maven_central
170 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
171 |
172 |
173 |
174 |
175 |
176 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/ExcelProperties.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.Arrays;
5 | import java.util.Comparator;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.UUID;
9 | import java.util.concurrent.ThreadPoolExecutor;
10 | import java.util.stream.Collectors;
11 | import java.util.stream.Stream;
12 |
13 | import org.apache.commons.collections.CollectionUtils;
14 | import org.apache.commons.lang3.StringUtils;
15 |
16 | import com.github.Dorae132.easyutil.easyexcel.export.ExcelCol;
17 | import com.github.Dorae132.easyutil.easyexcel.export.IDataSupplier;
18 | import com.github.Dorae132.easyutil.easyexcel.export.IWorkbookProcessor;
19 | import com.github.Dorae132.easyutil.easyexcel.read.IReadDoneCallBackProcessor;
20 | import com.google.common.collect.Lists;
21 | import com.google.common.collect.Maps;
22 |
23 | /**
24 | * preperties if the excel which would to be extported
25 | *
26 | * @author Dorae
27 | *
28 | * @param the data type
29 | * @param the type of the return value that excepted
30 | */
31 | public class ExcelProperties {
32 |
33 | // sheet名称和表头值
34 | private String sheetName = "sheet1";
35 |
36 | // 表头List
37 | private List titles;
38 |
39 | // 对象集合
40 | private List dataList;
41 |
42 | // data类的field
43 | private List fields;
44 |
45 | // fieldName -> field
46 | private Map fieldNameMap;
47 |
48 | // 文件地址
49 | private String filePath = "./easyexcel/";
50 |
51 | // 文件名
52 | private String fileName = new StringBuilder(UUID.randomUUID().toString()).append(".xlsx").toString().replace("-", "");
53 |
54 | // 行偏移
55 | private int rowOffset = 0;
56 |
57 | // 列偏移
58 | private int colOffset = 0;
59 |
60 | // 缓冲
61 | private int rowAccessWindowsize = 100;
62 |
63 | // 读模式,线程等待最大时长/秒
64 | private int readThreadWaitTime = 300;
65 |
66 | // 并行读线程池,默认实现异步ExcelUtils
67 | private ThreadPoolExecutor readThreadPool = null;
68 |
69 | // 并行写线程池
70 | private ThreadPoolExecutor writeThreadPool = null;
71 |
72 | private IWorkbookProcessor wbProcessor;
73 |
74 | // 读模式结束回调
75 | private IReadDoneCallBackProcessor readDoneCallBackProcessor;
76 |
77 | private IDataSupplier dataSupplier;
78 |
79 | private Class dataClazz;
80 |
81 | public static ExcelProperties produceCommonProperties(String sheetName, List> dataList, String filePath,
82 | String fileName, int colOffset, Class dataClazz, int rowAccessWindowsize, IWorkbookProcessor wbProcessor) throws Exception {
83 | return new ExcelProperties<>(sheetName, dataList, filePath, fileName, 0, colOffset, dataClazz, rowAccessWindowsize, wbProcessor, null);
84 | }
85 |
86 | public static ExcelProperties produceAppendProperties(String sheetName, String filePath, String fileName,
87 | int colOffset, Class dataClazz, int rowAccessWindowsize, IWorkbookProcessor wbProcessor, IDataSupplier> dataSupplier)
88 | throws Exception {
89 | return new ExcelProperties<>(sheetName, null, filePath, fileName, 0, colOffset, dataClazz, rowAccessWindowsize, wbProcessor, dataSupplier);
90 | }
91 |
92 | public static ExcelProperties produceReadProperties(String filePath, String fileName) {
93 | ExcelProperties excelProperties = new ExcelProperties();
94 | excelProperties.setFilePath(filePath);
95 | excelProperties.setFileName(fileName);
96 | return excelProperties;
97 | }
98 | /**
99 | *
100 | * @param sheetName
101 | * @param titleToFieldObjs 表头与字段的对应关系
102 | * @param dataList 数据集
103 | * @param filePath 文件路径(默认./liveunionExcels/)
104 | * @param fileName 文件名(默认随机)
105 | * @param excludeFields 要排除的字段名
106 | * @param rowOffset 行偏移
107 | * @param colOffset 列偏移
108 | * @param processor 结果processor
109 | * @param dataSupplier 数据源
110 | * @param dataClazz 资源类型
111 | * @param rowAccessWindowsize 缓冲大小
112 | * @throws Exception
113 | */
114 | @SuppressWarnings("unchecked")
115 | private ExcelProperties(String sheetName, List dataList, String filePath, String fileName, int rowOffset,
116 | int colOffset, Class dataClazz, int rowAccessWindowsize, IWorkbookProcessor wbProcessor,
117 | IDataSupplier dataSupplier) throws Exception {
118 | super();
119 | // 1.check
120 | if (CollectionUtils.isEmpty(dataList) && dataClazz == null) {
121 | // 保障能获取到资源类型
122 | throw new RuntimeException("dataList和资源数据类型不能同时为空!");
123 | }
124 | // 2.common field
125 | if (StringUtils.isNotBlank(sheetName)) {
126 | this.sheetName = sheetName;
127 | }
128 | this.dataList = dataList;
129 | if (StringUtils.isNotBlank(filePath)) {
130 | this.filePath = filePath;
131 | }
132 | if (StringUtils.isNotBlank(fileName)) {
133 | int sufixIndex = fileName.lastIndexOf(".");
134 | sufixIndex = sufixIndex == -1 ? fileName.length() : sufixIndex;
135 | this.fileName = new StringBuilder(fileName)
136 | .insert(sufixIndex, "_" + UUID.randomUUID().toString().replace("-", "")).toString();
137 | }
138 | this.rowOffset = rowOffset;
139 | this.colOffset = colOffset;
140 | this.wbProcessor = wbProcessor;
141 | this.dataSupplier = dataSupplier;
142 | this.rowAccessWindowsize = (rowAccessWindowsize > 100 ? rowAccessWindowsize : 100);
143 | // 3.special field
144 | this.dataClazz = dataClazz == null ? dataList.get(0).getClass() : dataClazz;
145 | Field[] declaredFields = this.dataClazz.getDeclaredFields();
146 | Arrays.sort(declaredFields, new Comparator() {
147 |
148 | @Override
149 | public int compare(Field o1, Field o2) {
150 | ExcelCol o1Annotation = o1.getAnnotation(ExcelCol.class);
151 | ExcelCol o2Annotation = o2.getAnnotation(ExcelCol.class);
152 | if (o1Annotation == null && o2Annotation == null) {
153 | return 0;
154 | } else if (o1Annotation != null
155 | && o2Annotation != null) {
156 | if (o1Annotation.order() == o2Annotation.order()) {
157 | return 0;
158 | } else {
159 | return o1Annotation.order() > o2Annotation.order() ? 1 : -1;
160 | }
161 | } else if (o1Annotation != null
162 | && o2Annotation == null) {
163 | return 1;
164 | } else {
165 | return -1;
166 | }
167 | }
168 | });
169 | this.fields = Lists.newArrayListWithCapacity(declaredFields.length);
170 | this.fieldNameMap = Maps.newHashMapWithExpectedSize(declaredFields.length);
171 | this.titles = Lists.newArrayListWithCapacity(declaredFields.length);
172 | for (Field declaredField : declaredFields) {
173 | ExcelCol excelCol = declaredField.getAnnotation(ExcelCol.class);
174 | if (excelCol == null) {
175 | continue;
176 | }
177 | this.fieldNameMap.put(declaredField.getName(), declaredField);
178 | titles.add(excelCol.title());
179 | this.fields.add(declaredField);
180 | }
181 | this.fieldNameMap = Stream.of(declaredFields).collect(Collectors.toMap(Field::getName, e -> e));
182 | }
183 |
184 | private ExcelProperties() {
185 | super();
186 | // TODO Auto-generated constructor stub
187 | }
188 |
189 | public int getRowAccessWindowsize() {
190 | return rowAccessWindowsize;
191 | }
192 |
193 | public void setRowAccessWindowsize(int rowAccessWindowsize) {
194 | this.rowAccessWindowsize = rowAccessWindowsize;
195 | }
196 |
197 | public String getSheetName() {
198 | return sheetName;
199 | }
200 |
201 | public void setSheetName(String sheetName) {
202 | this.sheetName = sheetName;
203 | }
204 |
205 | public List getTitles() {
206 | return titles;
207 | }
208 |
209 | public void setTitles(List titles) {
210 | this.titles = titles;
211 | }
212 |
213 | public List getDataList() {
214 | return dataList;
215 | }
216 |
217 | public void setDataList(List dataList) {
218 | this.dataList = dataList;
219 | }
220 |
221 | public List getFields() {
222 | return fields;
223 | }
224 |
225 | public void setFields(List fields) {
226 | this.fields = fields;
227 | }
228 |
229 | public Map getFieldNameMap() {
230 | return fieldNameMap;
231 | }
232 |
233 | public void setFieldNameMap(Map fieldNameMap) {
234 | this.fieldNameMap = fieldNameMap;
235 | }
236 |
237 | public String getFilePath() {
238 | return filePath;
239 | }
240 |
241 | public void setFilePath(String filePath) {
242 | this.filePath = filePath;
243 | }
244 |
245 | public String getFileName() {
246 | return fileName;
247 | }
248 |
249 | public void setFileName(String fileName) {
250 | this.fileName = fileName;
251 | }
252 |
253 | public int getRowOffset() {
254 | return rowOffset;
255 | }
256 |
257 | public void setRowOffset(int rowOffset) {
258 | this.rowOffset = rowOffset;
259 | }
260 |
261 | public int getColOffset() {
262 | return colOffset;
263 | }
264 |
265 | public void setColOffset(int colOffset) {
266 | this.colOffset = colOffset;
267 | }
268 |
269 | public IWorkbookProcessor getWbProcessor() {
270 | return wbProcessor;
271 | }
272 |
273 | public void setWbProcessor(IWorkbookProcessor wbProcessor) {
274 | this.wbProcessor = wbProcessor;
275 | }
276 |
277 | public IDataSupplier getDataSupplier() {
278 | return dataSupplier;
279 | }
280 |
281 | public void setDataSupplier(IDataSupplier dataSupplier) {
282 | this.dataSupplier = dataSupplier;
283 | }
284 |
285 | public Class getDataClazz() {
286 | return dataClazz;
287 | }
288 |
289 | public void setDataClazz(Class dataClazz) {
290 | this.dataClazz = dataClazz;
291 | }
292 |
293 | public int getReadThreadWaitTime() {
294 | return readThreadWaitTime;
295 | }
296 |
297 | public void setReadThreadWaitTime(int readThreadWaitTime) {
298 | this.readThreadWaitTime = readThreadWaitTime;
299 | }
300 |
301 | public ThreadPoolExecutor getReadThreadPool() {
302 | return readThreadPool;
303 | }
304 |
305 | public void setReadThreadPool(ThreadPoolExecutor readThreadPool) {
306 | this.readThreadPool = readThreadPool;
307 | }
308 |
309 | public ThreadPoolExecutor getWriteThreadPool() {
310 | return writeThreadPool;
311 | }
312 |
313 | public void setWriteThreadPool(ThreadPoolExecutor writeThreadPool) {
314 | this.writeThreadPool = writeThreadPool;
315 | }
316 |
317 | public IReadDoneCallBackProcessor getReadDoneCallBackProcessor() {
318 | return readDoneCallBackProcessor;
319 | }
320 |
321 | public void setReadDoneCallBackProcessor(IReadDoneCallBackProcessor readDoneCallBackProcessor) {
322 | this.readDoneCallBackProcessor = readDoneCallBackProcessor;
323 | }
324 |
325 | }
326 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/ExcelUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel;
2 |
3 | import java.util.concurrent.CountDownLatch;
4 | import java.util.concurrent.LinkedBlockingQueue;
5 | import java.util.concurrent.ThreadPoolExecutor;
6 | import java.util.concurrent.TimeUnit;
7 |
8 | import org.apache.poi.ss.usermodel.Sheet;
9 | import org.apache.poi.xssf.streaming.SXSSFWorkbook;
10 | import org.apache.poi.xssf.usermodel.XSSFWorkbook;
11 |
12 | import com.github.Dorae132.easyutil.easyexcel.common.EasyExcelException;
13 | import com.github.Dorae132.easyutil.easyexcel.common.Pair;
14 | import com.github.Dorae132.easyutil.easyexcel.export.IFillSheet;
15 | import com.github.Dorae132.easyutil.easyexcel.read.ConsumeRowThread;
16 | import com.github.Dorae132.easyutil.easyexcel.read.DefaultReadDoneCallBackProcessor;
17 | import com.github.Dorae132.easyutil.easyexcel.read.ExcelVersionEnums;
18 | import com.github.Dorae132.easyutil.easyexcel.read.IReadDoneCallBack;
19 | import com.github.Dorae132.easyutil.easyexcel.read.IReadDoneCallBackProcessor;
20 | import com.github.Dorae132.easyutil.easyexcel.read.IRowConsumer;
21 | import com.github.Dorae132.easyutil.easyexcel.read.event.IHandlerContext;
22 |
23 | /**
24 | * Excel utils
25 | *
26 | * @author Dorae
27 | *
28 | */
29 | public class ExcelUtils {
30 |
31 | // 并行读默认线程池
32 | private static final ThreadPoolExecutor READ_THREAD_POOL = new ThreadPoolExecutor(0, 2, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1024));
33 |
34 | /**
35 | * support the append strategy, but not rocommend, please focus on
36 | * FillSheetModeEnums.APPEND_MODE
37 | *
38 | * @param properties
39 | * @param iFillSheet
40 | * @return
41 | * @throws Exception
42 | */
43 | public static R excelExport(ExcelProperties properties, IFillSheet iFillSheet) throws Exception {
44 | // 2.写入文件
45 | SXSSFWorkbook sxssfWorkbook = null;
46 | XSSFWorkbook xssfWorkbook = null;
47 | try {
48 | xssfWorkbook = new XSSFWorkbook();;
49 | sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook, properties.getRowAccessWindowsize());
50 | Sheet sheet = sxssfWorkbook.getSheet(properties.getSheetName());
51 | if (sheet == null) {
52 | sheet = sxssfWorkbook.createSheet(properties.getSheetName());
53 | }
54 | iFillSheet.fill(properties, sheet);
55 | if (properties.getWbProcessor() != null) {
56 | return properties.getWbProcessor().process(sxssfWorkbook, properties);
57 | } else {
58 | return (R)sxssfWorkbook;
59 | }
60 | } catch (Exception e) {
61 | throw new EasyExcelException(e);
62 | } finally {
63 | if (properties.getWbProcessor() != null) {
64 | if (xssfWorkbook != null) {
65 | xssfWorkbook.close();
66 | }
67 | if (sxssfWorkbook != null) {
68 | sxssfWorkbook.close();
69 | }
70 | }
71 | }
72 | }
73 |
74 | /**
75 | * read util, enable multi sheet
76 | * when the syncCurrentThread is false and the callBack is not null, not recomand
77 | *
78 | * @param properties
79 | * @param rowConsumer
80 | * the consumer of a rowList
81 | * @param callBack
82 | * if null do nothing
83 | * @param threadCount
84 | * the number of the consume thread
85 | * @param syncCurrentThread
86 | * synchronized the current thread or not
87 | * @throws Exception
88 | */
89 | public static void excelRead(ExcelProperties properties, IRowConsumer rowConsumer, IReadDoneCallBack callBack,
90 | int threadCount, boolean syncCurrentThread) throws Exception {
91 | IHandlerContext context = ExcelVersionEnums.produceContext(properties);
92 | excelRead(properties, context, rowConsumer, callBack, threadCount, syncCurrentThread);
93 | }
94 |
95 | /**
96 | * You can expand the context use this
97 | * when the syncCurrentThread is false and the callBack is not null, not recomand
98 | * @param context
99 | * @param rowConsumer
100 | * @param callBack
101 | * @param threadCount
102 | * @param syncCurrentThread
103 | * @throws Exception
104 | */
105 | public static void excelRead(ExcelProperties properties, IHandlerContext context, IRowConsumer rowConsumer, IReadDoneCallBack callBack,
106 | int threadCount, boolean syncCurrentThread) throws Exception {
107 | CountDownLatch latch = new CountDownLatch(threadCount);
108 | try {
109 | for (int i = 0; i < threadCount; i++) {
110 | if (properties.getReadThreadPool() != null) {
111 | properties.getReadThreadPool().execute(new ConsumeRowThread<>(context, rowConsumer, latch));
112 | } else {
113 | // default impl
114 | READ_THREAD_POOL.execute(new ConsumeRowThread(context, rowConsumer, latch));
115 | }
116 | }
117 | // main thread get the rows
118 | context.process();
119 | if (syncCurrentThread) {
120 | latch.await(properties.getReadThreadWaitTime(), TimeUnit.SECONDS);
121 | if (callBack != null) {
122 | callBack.call();
123 | }
124 | } else if (callBack != null) {
125 | // not recomand, because there will be a new thread for every request.
126 | IReadDoneCallBackProcessor readDoneCallBackProcessor = properties.getReadDoneCallBackProcessor();
127 | if (readDoneCallBackProcessor == null) {
128 | readDoneCallBackProcessor = new DefaultReadDoneCallBackProcessor();
129 | }
130 | readDoneCallBackProcessor.process(Pair.of(latch, callBack), properties);
131 | }
132 | } catch (Exception e) {
133 | throw new EasyExcelException(e);
134 | } finally {
135 | context.close();
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/common/EasyExcelException.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.common;
2 |
3 | public class EasyExcelException extends RuntimeException {
4 | private static final long serialVersionUID = -20778884061304669L;
5 |
6 | public EasyExcelException() {
7 | super();
8 | }
9 |
10 | public EasyExcelException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
11 | super(message, cause, enableSuppression, writableStackTrace);
12 | }
13 |
14 | public EasyExcelException(String message, Throwable cause) {
15 | super(message, cause);
16 | }
17 |
18 | public EasyExcelException(String message) {
19 | super(message);
20 | }
21 |
22 | public EasyExcelException(Throwable cause) {
23 | super(cause);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/common/Pair.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.common;
2 |
3 | /**
4 | *
5 | * @author Dorae
6 | *
7 | * @param first
8 | * @param second
9 | */
10 | public class Pair {
11 |
12 | private S first;
13 | private T second;
14 |
15 | private Pair(S first, T second) {
16 | this.first = first;
17 | this.second = second;
18 | }
19 |
20 | public static Pair of(S first, T second) {
21 | return new Pair(first, second);
22 | }
23 |
24 | public S getFirst() {
25 | return first;
26 | }
27 |
28 | public T getSecond() {
29 | return second;
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/export/DefaultWorkbookProcessor.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.export;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 |
6 | import org.apache.poi.ss.usermodel.Workbook;
7 |
8 | import com.github.Dorae132.easyutil.easyexcel.ExcelProperties;
9 | import com.github.Dorae132.easyutil.easyexcel.common.EasyExcelException;
10 |
11 | /**
12 | * the default impl of
13 | * @author Dorae
14 | *
15 | */
16 | public class DefaultWorkbookProcessor implements IWorkbookProcessor{
17 |
18 | @Override
19 | public File process(Workbook wb, ExcelProperties, File> properties) throws Exception {
20 | // 1.创建目录
21 | validateFileDir(properties.getFilePath());
22 | File file = new File(new StringBuilder(properties.getFilePath()).append(properties.getFileName()).toString());
23 | FileOutputStream out = null;
24 | try {
25 | out = new FileOutputStream(file);
26 | wb.write(out);
27 | } catch (Exception e) {
28 | // do nothing
29 | throw new EasyExcelException(e);
30 | } finally {
31 | if (out != null) {
32 | out.close();
33 | }
34 | if (wb != null) {
35 | wb.close();
36 | }
37 | }
38 | return file;
39 | }
40 |
41 | /**
42 | * 校验目录是否存在
43 | *
44 | * @param filePath
45 | */
46 | private static synchronized void validateFileDir(String filePath) {
47 | File tempDir = new File(filePath);
48 | if (!tempDir.exists() && !tempDir.isDirectory()) {
49 | tempDir.mkdir();
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/export/ExcelCol.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.export;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * for the map of the excel and meta
10 | * @author Dorae
11 | *
12 | */
13 | @Retention(RetentionPolicy.RUNTIME)
14 | @Target({ElementType.FIELD})
15 | public @interface ExcelCol {
16 |
17 | // 对应列标题
18 | String title();
19 |
20 | int order() default 0;
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/export/FillSheetModeEnums.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.export;
2 |
3 | import java.util.List;
4 | import java.util.concurrent.ExecutorService;
5 | import java.util.concurrent.LinkedBlockingQueue;
6 | import java.util.concurrent.ThreadPoolExecutor;
7 | import java.util.concurrent.TimeUnit;
8 | import java.util.concurrent.atomic.AtomicBoolean;
9 |
10 | import org.apache.commons.collections.CollectionUtils;
11 | import org.apache.poi.ss.usermodel.Sheet;
12 |
13 | import com.github.Dorae132.easyutil.easyexcel.ExcelProperties;
14 | import com.github.Dorae132.easyutil.easyexcel.common.EasyExcelException;
15 | import com.github.Dorae132.easyutil.easyexcel.common.Pair;
16 |
17 | /**
18 | * 写入模式枚举
19 | *
20 | * @author Dorae
21 | *
22 | */
23 | public enum FillSheetModeEnums {
24 |
25 | /**
26 | * 普通模式
27 | */
28 | COMMON_MODE(new IFillSheet() {
29 |
30 | @Override
31 | public void fill(ExcelProperties excelProperties, Sheet sheet) throws Exception {
32 | createHeadRow(excelProperties, sheet);
33 | fillContentRow(excelProperties, sheet);
34 | }
35 |
36 | }),
37 | /**
38 | * 追加模式
39 | */
40 | APPEND_MODE(new IFillSheet() {
41 | @Override
42 | public void fill(ExcelProperties properties, Sheet sheet) throws Exception {
43 | int rowOffset = 0;
44 | Pair datasPair = null;
45 | List dataList = null;
46 | boolean hasNext = false;
47 | IDataSupplier dataSupplier = properties.getDataSupplier();
48 | while (dataSupplier != null) {
49 | datasPair = dataSupplier.getDatas();
50 | dataList = (List>) datasPair.getFirst();
51 | hasNext = (boolean) datasPair.getSecond();
52 | if (CollectionUtils.isNotEmpty(dataList)) {
53 | properties.setDataList(dataList);
54 | properties.setRowOffset(rowOffset);
55 | createHeadRow(properties, sheet);
56 | fillContentRow(properties, sheet);
57 | rowOffset += dataList.size();
58 | } else {
59 | break;
60 | }
61 | if (!hasNext) {
62 | break;
63 | }
64 | }
65 | }
66 |
67 | }),
68 | /**
69 | * 并行追加
70 | */
71 | PARALLEL_APPEND_MODE(new IFillSheet() {
72 | ExecutorService executorService = new ThreadPoolExecutor(0, 2, 60L, TimeUnit.SECONDS,
73 | new LinkedBlockingQueue<>(1024));
74 | @Override
75 | public void fill(ExcelProperties properties, Sheet sheet) throws Exception {
76 | int rowOffset = 0;
77 | IDataSupplier dataSupplier = properties.getDataSupplier();
78 | LinkedBlockingQueue dataPairQueue = new LinkedBlockingQueue<>();
79 | // more data or not
80 | AtomicBoolean moreData = new AtomicBoolean(true);
81 | // the get data thread
82 | Runnable getDataThread = new Runnable() {
83 | @Override
84 | public void run() {
85 | try {
86 | while (dataSupplier != null) {
87 | Pair pair = dataSupplier.getDatas();
88 | List dataList = (List) pair.getFirst();
89 | boolean hasNext = (boolean) pair.getSecond();
90 | if (CollectionUtils.isNotEmpty(dataList)) {
91 | dataPairQueue.put(dataList);
92 | } else {
93 | break;
94 | }
95 | if (!hasNext) {
96 | break;
97 | }
98 | }
99 | } catch (Exception e) {
100 | // just return
101 | throw new EasyExcelException(e);
102 | }
103 | // the thread that be used to get data return
104 | moreData.set(false);
105 | }
106 | };
107 | // start
108 | if (properties.getWriteThreadPool() != null) {
109 | properties.getWriteThreadPool().execute(getDataThread);
110 | } else {
111 | executorService.execute(getDataThread);
112 | }
113 | // main thread fill the excel
114 | while (true) {
115 | List dataList = (List) dataPairQueue.poll();
116 | if (CollectionUtils.isEmpty(dataList)) {
117 | if (moreData.get()) {
118 | // there are more data then wait
119 | TimeUnit.MILLISECONDS.sleep(100);
120 | continue;
121 | } else {
122 | // no more data then return
123 | break;
124 | }
125 | }
126 | properties.setDataList(dataList);
127 | properties.setRowOffset(rowOffset);
128 | createHeadRow(properties, sheet);
129 | fillContentRow(properties, sheet);
130 | rowOffset += dataList.size();
131 | }
132 | }
133 |
134 | });
135 |
136 | private IFillSheet iFillSheet;
137 |
138 | private FillSheetModeEnums(IFillSheet iFillSheet) {
139 | this.iFillSheet = iFillSheet;
140 | }
141 |
142 | public IFillSheet getValue() {
143 | return this.iFillSheet;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/export/IDataSupplier.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.export;
2 |
3 | import java.util.List;
4 |
5 | import com.github.Dorae132.easyutil.easyexcel.common.Pair;
6 |
7 | /**
8 | * for the append strategy
9 | * @author Dorae
10 | *
11 | * @param
12 | */
13 | public interface IDataSupplier {
14 |
15 | public Pair, Boolean> getDatas();
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/export/IFillSheet.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.export;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | import org.apache.commons.collections.CollectionUtils;
8 | import org.apache.poi.ss.usermodel.Row;
9 | import org.apache.poi.ss.usermodel.Sheet;
10 | import org.apache.poi.ss.util.CellUtil;
11 |
12 | import com.github.Dorae132.easyutil.easyexcel.ExcelProperties;
13 |
14 | /**
15 | * 填充sheet
16 | * @author Dorae
17 | *
18 | */
19 | public interface IFillSheet {
20 |
21 | void fill(ExcelProperties excelProperties, Sheet sheet) throws Exception;
22 |
23 | /**
24 | * 填充内容
25 | *
26 | * @param dataList
27 | * @param titleToFieldObjs
28 | * @param sheet
29 | * @throws IllegalAccessException
30 | * @throws IllegalArgumentException
31 | */
32 | default void fillContentRow(ExcelProperties, ?> excelProperties, Sheet sheet) throws Exception {
33 | if (CollectionUtils.isEmpty(excelProperties.getDataList())) {
34 | return;
35 | }
36 | List fields = excelProperties.getFields();
37 | Map fieldNameMap = excelProperties.getFieldNameMap();
38 | int row = 1 + excelProperties.getRowOffset();
39 | for (Object object : excelProperties.getDataList()) {
40 | int col = 0;
41 | Row createRow = sheet.createRow(row);
42 | for (Field field : fields) {
43 | field.setAccessible(true);
44 | Object value = field.get(object);
45 | CellUtil.createCell(createRow, col, value == null ? "" : value.toString());
46 | col++;
47 | }
48 | row++;
49 | }
50 | }
51 |
52 | /**
53 | * 创建表头行(第0行创建)
54 | *
55 | * @param titleMap
56 | * 对象属性名称->表头显示名称
57 | */
58 | default void createHeadRow(ExcelProperties properties, Sheet sheet) {
59 | List titles = properties.getTitles();
60 | // 偏移不为0
61 | if (properties.getRowOffset() != 0) {
62 | return;
63 | }
64 | Row headRow = sheet.createRow(0);
65 | int i = 0;
66 | for (String title : titles) {
67 | headRow.createCell(i++).setCellValue(title);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/export/IWorkbookProcessor.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.export;
2 |
3 | import org.apache.poi.ss.usermodel.Workbook;
4 |
5 | import com.github.Dorae132.easyutil.easyexcel.ExcelProperties;
6 |
7 | /**
8 | * process the file that has been created
9 | * @author Dorae
10 | *
11 | * @param
12 | * @param
13 | */
14 | @FunctionalInterface
15 | public interface IWorkbookProcessor {
16 | R process(Workbook wb, ExcelProperties, R> properties) throws Exception;
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/ConsumeRowThread.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read;
2 |
3 | import java.util.List;
4 | import java.util.concurrent.BrokenBarrierException;
5 | import java.util.concurrent.CountDownLatch;
6 | import java.util.concurrent.CyclicBarrier;
7 | import java.util.concurrent.TimeUnit;
8 |
9 | import org.apache.commons.collections.CollectionUtils;
10 |
11 | import com.github.Dorae132.easyutil.easyexcel.common.EasyExcelException;
12 | import com.github.Dorae132.easyutil.easyexcel.read.event.IHandlerContext;
13 |
14 | /**
15 | * 消费rowthread
16 | *
17 | * @author Dorae
18 | *
19 | */
20 | public class ConsumeRowThread implements Runnable {
21 |
22 | private IHandlerContext context;
23 |
24 | private IRowConsumer rowConsumer;
25 |
26 | private CountDownLatch lanch;
27 |
28 | @Override
29 | public void run() {
30 | try {
31 | while (true) {
32 | List row = null;
33 | row = context.getRow();
34 | if (CollectionUtils.isEmpty(row)) {
35 | if (!context.isFileEnded()) {
36 | // there are more rows
37 | TimeUnit.MILLISECONDS.sleep(100);
38 | continue;
39 | } else {
40 | // there are no more rows
41 | break;
42 | }
43 | }
44 | rowConsumer.consume(row);
45 | }
46 | } catch (Exception e) {
47 | throw new EasyExcelException(e);
48 | } finally {
49 | if (lanch != null) {
50 | lanch.countDown();
51 | }
52 | }
53 | }
54 |
55 | public ConsumeRowThread(IHandlerContext context, IRowConsumer rowConsumer, CountDownLatch latch) {
56 | super();
57 | this.context = context;
58 | this.rowConsumer = rowConsumer;
59 | this.lanch = latch;
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/DefaultReadDoneCallBackProcessor.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read;
2 |
3 | import java.util.concurrent.CountDownLatch;
4 | import java.util.concurrent.TimeUnit;
5 |
6 | import com.github.Dorae132.easyutil.easyexcel.ExcelProperties;
7 | import com.github.Dorae132.easyutil.easyexcel.common.EasyExcelException;
8 | import com.github.Dorae132.easyutil.easyexcel.common.Pair;
9 |
10 | /**
11 | * the default processor
12 | * this implemet will not be recomanded.
13 | * @author Dorae
14 | *
15 | */
16 | public class DefaultReadDoneCallBackProcessor implements IReadDoneCallBackProcessor {
17 |
18 | @Override
19 | public Void process(Pair callback, ExcelProperties, Void> properties)
20 | throws Exception {
21 | new Thread(() -> {
22 | try {
23 | callback.getFirst().await(properties.getReadThreadWaitTime(), TimeUnit.SECONDS);
24 | } catch (Exception e) {
25 | throw new EasyExcelException(e);
26 | }
27 | callback.getSecond().call();
28 | }).start();
29 | return null;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/ExcelVersionEnums.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read;
2 |
3 | import java.io.FileInputStream;
4 |
5 | import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
6 | import org.apache.poi.hssf.eventusermodel.HSSFRequest;
7 | import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
8 | import org.apache.poi.poifs.filesystem.POIFSFileSystem;
9 |
10 | import com.github.Dorae132.easyutil.easyexcel.ExcelProperties;
11 | import com.github.Dorae132.easyutil.easyexcel.read.event.IHandlerContext;
12 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.Default03RecordHandlerContext;
13 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel07.Default07RecordHandlerContext;
14 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel07.XlsxHandler;
15 |
16 | /**
17 | * the enums of the excel versions
18 | *
19 | * @author Dorae
20 | *
21 | */
22 | public enum ExcelVersionEnums {
23 |
24 | V2003("xls"), V2007("xlsx");
25 |
26 | private String suffix;
27 |
28 | private ExcelVersionEnums(String suffix) {
29 | this.suffix = suffix;
30 | }
31 |
32 | public String getSuffix() {
33 | return suffix;
34 | }
35 |
36 | public static IHandlerContext produceContext(ExcelProperties properties) throws Exception {
37 | StringBuilder fileNameSB = new StringBuilder(properties.getFileName());
38 | String fileNameSufix = fileNameSB.substring(fileNameSB.lastIndexOf(".") + 1, fileNameSB.length());
39 | String absolutePath = fileNameSB.insert(0, properties.getFilePath()).toString();
40 | try {
41 | if (V2003.getSuffix().equals(fileNameSufix)) {
42 | FileInputStream inputStream = new FileInputStream(absolutePath);
43 | HSSFRequest request = new HSSFRequest();
44 | POIFSFileSystem fileSystem = new POIFSFileSystem(inputStream);
45 | Default03RecordHandlerContext context = Default03RecordHandlerContext.Default03RecordContextFactory
46 | .getContext(request, fileSystem);
47 | MissingRecordAwareHSSFListener missingRecordAwareHSSFListener = new MissingRecordAwareHSSFListener(context);
48 | FormatTrackingHSSFListener formatTrackingHSSFListener = new FormatTrackingHSSFListener(
49 | missingRecordAwareHSSFListener);
50 | request.addListenerForAllRecords(formatTrackingHSSFListener);
51 | // hssfRequest.addListenerForAllRecords(new
52 | // SheetRecordCollectingListener(formatTrackingHSSFListener));
53 | return context;
54 | } else if (V2007.getSuffix().equals(fileNameSufix)) {
55 | XlsxHandler xlsxHandler = new XlsxHandler();
56 | Default07RecordHandlerContext context = Default07RecordHandlerContext.Default07RecordContextFactory
57 | .getContext(xlsxHandler, absolutePath);
58 | xlsxHandler.setContext(context);
59 | return context;
60 | } else {
61 | throw new RuntimeException("不支持的文件类型");
62 | }
63 | } finally {
64 | // the stream will be closed int the context.
65 | }
66 | };
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/IReadDoneCallBack.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read;
2 |
3 | /**
4 | * this while be called when there are no more rows
5 | * @author Dorae
6 | * @param
7 | */
8 | public interface IReadDoneCallBack {
9 |
10 | R call();
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/IReadDoneCallBackProcessor.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read;
2 |
3 | import java.util.concurrent.CountDownLatch;
4 |
5 | import com.github.Dorae132.easyutil.easyexcel.ExcelProperties;
6 | import com.github.Dorae132.easyutil.easyexcel.common.Pair;
7 |
8 | /**
9 | * the processor that will be called after the read thread have been done.
10 | * @author Dorae
11 | *
12 | */
13 | public interface IReadDoneCallBackProcessor {
14 |
15 | R process(Pair callback, ExcelProperties, C> properties) throws Exception;
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/IRowConsumer.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * The consumer of the row
7 | * @author Dorae
8 | * @param
9 | */
10 | public interface IRowConsumer {
11 | void consume(List row);
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/IHandlerContext.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event;
2 |
3 | import java.io.Closeable;
4 | import java.util.List;
5 |
6 | /**
7 | * handlerContext顶层接口
8 | *
9 | * @author Dorae
10 | *
11 | * @param
12 | * The type that wanted in the cell
13 | */
14 | public interface IHandlerContext extends Closeable {
15 |
16 | /**
17 | * produce a new row
18 | *
19 | * @param row
20 | * @throws InterruptedException
21 | */
22 | void newRow(List row) throws Exception;
23 |
24 | /**
25 | * get a row from the context
26 | *
27 | * @return
28 | * @throws Exception
29 | */
30 | List getRow() throws Exception;
31 |
32 | /**
33 | * Fire this when the file is ending.
34 | *
35 | * @return
36 | */
37 | boolean fileEnd();
38 |
39 | /**
40 | * the processing is end or not
41 | *
42 | * @return
43 | */
44 | boolean isFileEnded();
45 |
46 | /**
47 | * process the file
48 | */
49 | void process() throws Exception;
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/Default03RecordHandlerContext.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03;
2 |
3 | import java.io.IOException;
4 | import java.util.List;
5 | import java.util.concurrent.LinkedBlockingQueue;
6 | import java.util.concurrent.atomic.AtomicInteger;
7 |
8 | import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
9 | import org.apache.poi.hssf.eventusermodel.HSSFListener;
10 | import org.apache.poi.hssf.eventusermodel.HSSFRequest;
11 | import org.apache.poi.hssf.record.Record;
12 | import org.apache.poi.hssf.record.SSTRecord;
13 | import org.apache.poi.poifs.filesystem.POIFSFileSystem;
14 |
15 | import com.github.Dorae132.easyutil.easyexcel.common.EasyExcelException;
16 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler.Abstract03RecordHandler;
17 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler.BlankRecordHandler;
18 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler.BoundSheetRecordHandler;
19 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler.EofRecordHandler;
20 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler.NumberRecordHandler;
21 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler.RowEndRecordHandler;
22 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler.SSTRecordHandler;
23 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler.StringRecordHandler;
24 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler.TailRecordHandler;
25 | import com.google.common.collect.Lists;
26 |
27 | /**
28 | * The defaut context for the handler
29 | * @author Dorae
30 | *
31 | */
32 | public class Default03RecordHandlerContext implements IRecordHandlerContext, HSSFListener {
33 |
34 | private int currColNum;
35 |
36 | private SSTRecord sstRecord;
37 |
38 | private List currRowList;
39 |
40 | private LinkedBlockingQueue> rowQueue;
41 |
42 | private Abstract03RecordHandler headRecordHandler;
43 |
44 | private AtomicInteger sheetNumbers = new AtomicInteger(1);
45 |
46 | private HSSFRequest request;
47 |
48 | private POIFSFileSystem fileSystem;
49 |
50 | public static class Default03RecordContextFactory {
51 | public static Default03RecordHandlerContext getContext(HSSFRequest request, POIFSFileSystem fileSystem) {
52 | Default03RecordHandlerContext context = new Default03RecordHandlerContext();
53 | context.currColNum = 0;
54 | context.currRowList = Lists.newArrayList();
55 | context.rowQueue = new LinkedBlockingQueue<>();
56 | context.initHeadHandler();
57 | context.request = request;
58 | context.fileSystem = fileSystem;
59 | return context;
60 | }
61 | }
62 |
63 | protected void initHeadHandler() {
64 | // The head handler do nothing, just pass
65 | headRecordHandler = new Abstract03RecordHandler(this) {
66 | @Override
67 | public void decode(Record record) throws Exception {
68 | }
69 | @Override
70 | public boolean couldDecode(Record record) {
71 | return false;
72 | }
73 | };
74 | SSTRecordHandler sstRecordHandler = new SSTRecordHandler(this);
75 | StringRecordHandler stringRecordHandler = new StringRecordHandler(this);
76 | NumberRecordHandler numberRecordHandler = new NumberRecordHandler(this);
77 | BlankRecordHandler blankRecordHandler = new BlankRecordHandler(this);
78 | RowEndRecordHandler rowEndRecordHandler = new RowEndRecordHandler(this);
79 | BoundSheetRecordHandler boundSheetRecordHandler = new BoundSheetRecordHandler(this);
80 | EofRecordHandler eofRecordHandler = new EofRecordHandler(this);
81 | TailRecordHandler tailRecordHandler = new TailRecordHandler(this);
82 | headRecordHandler.setNext(sstRecordHandler).setNext(stringRecordHandler).setNext(numberRecordHandler)
83 | .setNext(blankRecordHandler).setNext(rowEndRecordHandler).setNext(boundSheetRecordHandler)
84 | .setNext(eofRecordHandler).setNext(tailRecordHandler);
85 | }
86 |
87 | private Default03RecordHandlerContext() {
88 | super();
89 | }
90 |
91 | @Override
92 | public void handle(Record record) throws Exception {
93 | this.headRecordHandler.handle(record);
94 | }
95 |
96 | @Override
97 | public void registRecordHandler(Abstract03RecordHandler recordHandler) {
98 | // Let the SSTRecordHandler has the highest priority.
99 | recordHandler.setNext(headRecordHandler.next.next);
100 | headRecordHandler.next.setNext(recordHandler);
101 | }
102 |
103 | @Override
104 | public void setCurrColNum(int currColNum) {
105 | this.currColNum = currColNum;
106 | }
107 |
108 | @Override
109 | public int getCurrColNum() {
110 | return this.currColNum;
111 | }
112 |
113 | @Override
114 | public SSTRecord getSSTRecord() {
115 | return this.sstRecord;
116 | }
117 |
118 | @Override
119 | public void initCurrRowList(int colNum) {
120 | if (colNum <= 0) {
121 | this.currRowList = Lists.newArrayList();
122 | } else {
123 | this.currRowList = Lists.newArrayListWithExpectedSize(colNum);
124 | }
125 | }
126 |
127 | @Override
128 | public List getCurrRowList() {
129 | return this.currRowList;
130 | }
131 |
132 | @Override
133 | public void addCol2CurrRowList(String colValue) {
134 | currRowList.add(colValue);
135 | }
136 |
137 | @Override
138 | public void setSSTRecord(SSTRecord sstRecord) {
139 | this.sstRecord = sstRecord;
140 | }
141 |
142 | @Override
143 | public void newRow(List row) throws InterruptedException {
144 | this.rowQueue.put(row);
145 | }
146 |
147 | @Override
148 | public List getRow() throws Exception {
149 | return this.rowQueue.poll();
150 | }
151 |
152 | @Override
153 | public boolean fileEnd() {
154 | return true;
155 | }
156 |
157 | @Override
158 | public boolean isFileEnded() {
159 | return sheetNumbers.intValue() == 0;
160 | }
161 |
162 | @Override
163 | public void increaseSheetNumbers() {
164 | sheetNumbers.incrementAndGet();
165 | }
166 |
167 | @Override
168 | public void decreaseSheetNumbers() {
169 | sheetNumbers.decrementAndGet();
170 | }
171 |
172 | @Override
173 | public void process() throws Exception {
174 | HSSFEventFactory hssfEventFactory = new HSSFEventFactory();
175 | hssfEventFactory.processWorkbookEvents(this.request, this.fileSystem);
176 | }
177 |
178 | @Override
179 | public void processRecord(Record record) {
180 | try {
181 | this.handle(record);
182 | } catch (Exception e) {
183 | throw new EasyExcelException(e);
184 | }
185 | }
186 |
187 | @Override
188 | public void close() throws IOException {
189 | if (fileSystem != null) {
190 | fileSystem.close();
191 | }
192 | }
193 |
194 | }
195 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/IRecordHandlerContext.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03;
2 |
3 | import java.util.List;
4 |
5 | import org.apache.poi.hssf.record.SSTRecord;
6 |
7 | import com.github.Dorae132.easyutil.easyexcel.read.event.IHandlerContext;
8 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler.Abstract03RecordHandler;
9 |
10 | /**
11 | * The interface for handlerContext
12 | * @author Dorae
13 | * @param The target type for the cell value
14 | */
15 | public interface IRecordHandlerContext extends IHandlerContext {
16 |
17 | /**
18 | * handle new record
19 | * @param record
20 | * @throws Exception
21 | */
22 | public void handle(R record) throws Exception;
23 |
24 | /**
25 | * Enable the capacity that could insert the custom handler
26 | * @param recordHandler
27 | */
28 | void registRecordHandler(Abstract03RecordHandler recordHandler);
29 |
30 | /**
31 | * Set the current col num.
32 | * @param currColNum
33 | */
34 | void setCurrColNum(int currColNum);
35 |
36 | /**
37 | * Get the current col num.
38 | * @return
39 | */
40 | int getCurrColNum();
41 |
42 | /**
43 | * Get the constants table
44 | * @return
45 | */
46 | SSTRecord getSSTRecord();
47 |
48 | /**
49 | * Init the current row list, let it be empty.
50 | * @param colNum
51 | */
52 | void initCurrRowList(int colNum);
53 |
54 | /**
55 | * Get the current row list
56 | * @return
57 | */
58 | List getCurrRowList();
59 |
60 | /**
61 | * Add a value of the col to the current row list.
62 | * @param t
63 | */
64 | void addCol2CurrRowList(C colValue);
65 |
66 | /**
67 | * Set the constants table
68 | * @param sstRecord
69 | */
70 | void setSSTRecord(SSTRecord sstRecord);
71 |
72 | /**
73 | * set the number of the sheet.
74 | * this is used to judge whither the file has been ended or not.
75 | * @param nums
76 | */
77 | void increaseSheetNumbers();
78 |
79 | /**
80 | * decrease the number of the sheet
81 | */
82 | void decreaseSheetNumbers();
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/XlsListener.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03;
2 |
3 | import org.apache.poi.hssf.eventusermodel.HSSFListener;
4 | import org.apache.poi.hssf.record.Record;
5 |
6 | /**
7 | * for xls
8 | *
9 | * @author Dorae
10 | *
11 | */
12 | @Deprecated
13 | public class XlsListener implements HSSFListener {
14 |
15 | private IRecordHandlerContext context = Default03RecordHandlerContext.Default03RecordContextFactory.getContext(null,
16 | null);
17 |
18 | @Override
19 | public void processRecord(Record record) {
20 | try {
21 | context.handle(record);
22 | } catch (Exception e) {
23 | // do nothing
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/handler/Abstract03RecordHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler;
2 |
3 | import org.apache.poi.hssf.record.Record;
4 |
5 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.IRecordHandlerContext;
6 |
7 | /**
8 | * the handler that process the record for the xls file
9 | * @author Dorae
10 | *
11 | * @param The target type of the cell.
12 | */
13 | public abstract class Abstract03RecordHandler {
14 |
15 | public Abstract03RecordHandler next;
16 |
17 | public IRecordHandlerContext handlerContext;
18 |
19 | private Abstract03RecordHandler() {
20 | super();
21 | }
22 |
23 | public Abstract03RecordHandler(IRecordHandlerContext handlerContext) {
24 | super();
25 | this.handlerContext = handlerContext;
26 | }
27 |
28 | public Abstract03RecordHandler setNext(Abstract03RecordHandler next) {
29 | this.next = next;
30 | return next;
31 | }
32 |
33 | /**
34 | * handle
35 | * @param handlerContext
36 | * @param record
37 | */
38 | public void handle(Record record) throws Exception {
39 | if (this.couldDecode(record)) {
40 | this.decode(record);
41 | } else if (next != null) {
42 | next.handle(record);
43 | } else {
44 | // just do nothing
45 | return;
46 | }
47 | }
48 |
49 | /**
50 | * could decode or not
51 | * @param handlerContext
52 | * @param record
53 | * @return
54 | */
55 | public abstract boolean couldDecode(Record record);
56 |
57 | /**
58 | * decode
59 | * @param handlerContext
60 | * @param record
61 | */
62 | public abstract void decode(Record record) throws Exception;
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/handler/BlankRecordHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler;
2 |
3 | import org.apache.poi.hssf.record.BlankRecord;
4 | import org.apache.poi.hssf.record.Record;
5 |
6 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.IRecordHandlerContext;
7 |
8 | /**
9 | * for blank cell
10 | * @author Dorae
11 | *
12 | */
13 | public class BlankRecordHandler extends Abstract03RecordHandler {
14 |
15 | private final static String BLANK = "";
16 |
17 | public BlankRecordHandler(IRecordHandlerContext handlerContext) {
18 | super(handlerContext);
19 | }
20 |
21 | @Override
22 | public boolean couldDecode(Record record) {
23 | return BlankRecord.sid == record.getSid();
24 | }
25 |
26 | @Override
27 | public void decode(Record record) {
28 | BlankRecord blankRecord = (BlankRecord) record;
29 | int currColNum = blankRecord.getColumn();
30 | handlerContext.addCol2CurrRowList(BLANK);
31 | handlerContext.setCurrColNum(currColNum);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/handler/BoundSheetRecordHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler;
2 |
3 | import org.apache.poi.hssf.record.BoundSheetRecord;
4 | import org.apache.poi.hssf.record.Record;
5 |
6 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.IRecordHandlerContext;
7 |
8 | /**
9 | * the bound of the workbook
10 | * @author Dorae
11 | *
12 | */
13 | public class BoundSheetRecordHandler extends Abstract03RecordHandler {
14 |
15 | public BoundSheetRecordHandler(IRecordHandlerContext handlerContext) {
16 | super(handlerContext);
17 | }
18 |
19 | @Override
20 | public boolean couldDecode(Record record) {
21 | return BoundSheetRecord.sid == record.getSid();
22 | }
23 |
24 | @Override
25 | public void decode(Record record) {
26 | handlerContext.increaseSheetNumbers();
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/handler/EofRecordHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler;
2 |
3 | import org.apache.poi.hssf.record.EOFRecord;
4 | import org.apache.poi.hssf.record.Record;
5 |
6 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.IRecordHandlerContext;
7 |
8 | /**
9 | * record the end of the file/sheet
10 | * @author Dorae
11 | *
12 | */
13 | public class EofRecordHandler extends Abstract03RecordHandler {
14 |
15 | public EofRecordHandler(IRecordHandlerContext handlerContext) {
16 | super(handlerContext);
17 | }
18 |
19 | @Override
20 | public boolean couldDecode(Record record) {
21 | return EOFRecord.sid == record.getSid();
22 | }
23 |
24 | @Override
25 | public void decode(Record record) throws Exception {
26 | handlerContext.decreaseSheetNumbers();
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/handler/NumberRecordHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler;
2 |
3 | import java.text.DecimalFormat;
4 |
5 | import org.apache.poi.hssf.record.NumberRecord;
6 | import org.apache.poi.hssf.record.Record;
7 |
8 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.IRecordHandlerContext;
9 |
10 | /**
11 | * The handler for number cell
12 | * @author Dorae
13 | *
14 | */
15 | public class NumberRecordHandler extends Abstract03RecordHandler {
16 |
17 | private static final DecimalFormat DF = new DecimalFormat("0.00");
18 |
19 | public NumberRecordHandler(IRecordHandlerContext handlerContext) {
20 | super(handlerContext);
21 | }
22 |
23 | @Override
24 | public boolean couldDecode(Record record) {
25 | return NumberRecord.sid == record.getSid();
26 | }
27 |
28 | @Override
29 | public void decode(Record record) {
30 | NumberRecord numberRecord = (NumberRecord) record;
31 | int currColNum = numberRecord.getColumn();
32 | handlerContext.addCol2CurrRowList(DF.format(numberRecord.getValue()));
33 | handlerContext.setCurrColNum(currColNum);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/handler/RowEndRecordHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler;
2 |
3 | import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
4 | import org.apache.poi.hssf.record.Record;
5 |
6 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.IRecordHandlerContext;
7 |
8 | /**
9 | * the row end handler
10 | * @author Dorae
11 | *
12 | */
13 | public class RowEndRecordHandler extends Abstract03RecordHandler {
14 |
15 | @Override
16 | public boolean couldDecode(Record record) {
17 | return record instanceof LastCellOfRowDummyRecord;
18 | }
19 |
20 | public RowEndRecordHandler(IRecordHandlerContext handlerContext) {
21 | super(handlerContext);
22 | }
23 |
24 | @Override
25 | public void decode(Record record) throws Exception {
26 | // 产生新行,结束当前行
27 | if (handlerContext.getCurrColNum() != 0) {
28 | handlerContext.newRow(handlerContext.getCurrRowList());
29 | handlerContext.setCurrColNum(0);
30 | }
31 | handlerContext.initCurrRowList(0);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/handler/SSTRecordHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler;
2 |
3 | import org.apache.poi.hssf.record.Record;
4 | import org.apache.poi.hssf.record.SSTRecord;
5 |
6 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.IRecordHandlerContext;
7 |
8 | /**
9 | * the handler for constants
10 | * excel中的常量表
11 | * @author Dorae
12 | *
13 | */
14 | public class SSTRecordHandler extends Abstract03RecordHandler {
15 |
16 | public SSTRecordHandler(IRecordHandlerContext handlerContext) {
17 | super(handlerContext);
18 | }
19 |
20 | @Override
21 | public boolean couldDecode(Record record) {
22 | return SSTRecord.sid == record.getSid();
23 | }
24 |
25 | @Override
26 | public void decode(Record record) {
27 | SSTRecord sstRecord = (SSTRecord) record;
28 | handlerContext.setSSTRecord(sstRecord);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/handler/StringRecordHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler;
2 |
3 | import org.apache.poi.hssf.record.LabelSSTRecord;
4 | import org.apache.poi.hssf.record.Record;
5 |
6 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.IRecordHandlerContext;
7 |
8 | /**
9 | * The handler for string cel.
10 | * @author Dorae
11 | *
12 | */
13 | public class StringRecordHandler extends Abstract03RecordHandler {
14 |
15 | public StringRecordHandler(IRecordHandlerContext handlerContext) {
16 | super(handlerContext);
17 | }
18 |
19 | @Override
20 | public boolean couldDecode(Record record) {
21 | return LabelSSTRecord.sid == record.getSid();
22 | }
23 |
24 | @Override
25 | public void decode(Record record) {
26 | LabelSSTRecord labelSSTRecord = (LabelSSTRecord) record;
27 | int currColNum = labelSSTRecord.getColumn();
28 | handlerContext.addCol2CurrRowList(handlerContext.getSSTRecord().getString(labelSSTRecord.getSSTIndex()).toString());
29 | handlerContext.setCurrColNum(currColNum);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel03/handler/TailRecordHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel03.handler;
2 |
3 | import org.apache.poi.hssf.record.Record;
4 |
5 | import com.github.Dorae132.easyutil.easyexcel.read.event.excel03.IRecordHandlerContext;
6 |
7 | /**
8 | * The tail handler, just in case there are no handler to be useed to resolve the col value.
9 | * @author Dorae
10 | *
11 | */
12 | public class TailRecordHandler extends Abstract03RecordHandler {
13 |
14 | public TailRecordHandler(IRecordHandlerContext handlerContext) {
15 | super(handlerContext);
16 | }
17 |
18 | @Override
19 | public boolean couldDecode(Record record) {
20 | return true;
21 | }
22 |
23 | @Override
24 | public void decode(Record record) throws Exception {
25 | // just discard the record
26 |
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel07/Default07RecordHandlerContext.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel07;
2 |
3 | import java.io.IOException;
4 | import java.util.List;
5 | import java.util.concurrent.LinkedBlockingQueue;
6 | import java.util.concurrent.atomic.AtomicBoolean;
7 |
8 | import com.github.Dorae132.easyutil.easyexcel.read.event.IHandlerContext;
9 |
10 | /**
11 | * The defaut context for the handler
12 | * @author Dorae
13 | *
14 | */
15 | public class Default07RecordHandlerContext implements IHandlerContext {
16 |
17 | private LinkedBlockingQueue> rowQueue;
18 |
19 | private XlsxHandler handler;
20 |
21 | private AtomicBoolean fileEndFlag = new AtomicBoolean(false);
22 |
23 | private String fileName;
24 |
25 | public static class Default07RecordContextFactory {
26 | public static Default07RecordHandlerContext getContext(XlsxHandler xlsxHandler, String fileName) {
27 | Default07RecordHandlerContext context = new Default07RecordHandlerContext();
28 | context.rowQueue = new LinkedBlockingQueue<>();
29 | context.handler = xlsxHandler;
30 | context.fileName = fileName;
31 | return context;
32 | }
33 | }
34 |
35 | private Default07RecordHandlerContext() {
36 | super();
37 | }
38 |
39 | @Override
40 | public void newRow(List row) throws Exception {
41 | this.rowQueue.put(row);
42 | }
43 |
44 | @Override
45 | public List getRow() throws Exception {
46 | return this.rowQueue.poll();
47 | }
48 |
49 | @Override
50 | public boolean fileEnd() {
51 | fileEndFlag.set(true);
52 | return fileEndFlag.get();
53 | }
54 |
55 | @Override
56 | public boolean isFileEnded() {
57 | return fileEndFlag.get();
58 | }
59 |
60 | @Override
61 | public void process() throws Exception {
62 | handler.process(fileName);
63 | }
64 |
65 | @Override
66 | public void close() throws IOException {
67 | // just do nothing
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/github/Dorae132/easyutil/easyexcel/read/event/excel07/XlsxHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel.read.event.excel07;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.util.Iterator;
6 | import java.util.List;
7 |
8 | import org.apache.commons.lang3.StringUtils;
9 | import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
10 | import org.apache.poi.openxml4j.opc.OPCPackage;
11 | import org.apache.poi.ss.usermodel.BuiltinFormats;
12 | import org.apache.poi.ss.usermodel.DataFormatter;
13 | import org.apache.poi.xssf.eventusermodel.XSSFReader;
14 | import org.apache.poi.xssf.model.SharedStringsTable;
15 | import org.apache.poi.xssf.model.StylesTable;
16 | import org.apache.poi.xssf.usermodel.XSSFCellStyle;
17 | import org.apache.poi.xssf.usermodel.XSSFRichTextString;
18 | import org.xml.sax.Attributes;
19 | import org.xml.sax.InputSource;
20 | import org.xml.sax.SAXException;
21 | import org.xml.sax.XMLReader;
22 | import org.xml.sax.helpers.DefaultHandler;
23 | import org.xml.sax.helpers.XMLReaderFactory;
24 |
25 | import com.github.Dorae132.easyutil.easyexcel.read.event.IHandlerContext;
26 | import com.google.common.collect.Lists;
27 |
28 | /**
29 | *
30 | * @author Dorae
31 | * @see 参考 https://www.cnblogs.com/wshsdlau/p/5643847.html
32 | *
33 | */
34 | public class XlsxHandler extends DefaultHandler {
35 |
36 | private IHandlerContext context;
37 |
38 | /**
39 | * 共享字符串表
40 | */
41 | private SharedStringsTable sharedStringsTable;
42 |
43 | /**
44 | * 上一次的内容
45 | */
46 | private StringBuffer lastContents = new StringBuffer();
47 |
48 | /**
49 | * 字符串标识
50 | */
51 | private boolean nextIsString;
52 |
53 | /**
54 | * 工作表索引
55 | */
56 | private int sheetIndex = -1;
57 |
58 | /**
59 | * 行集合
60 | */
61 | private List currRowList = Lists.newArrayList();
62 |
63 | /**
64 | * 当前行
65 | */
66 | private int currRowIndex = 0;
67 |
68 | /**
69 | * 当前列
70 | */
71 | private int currColIndex = 0;
72 |
73 | /**
74 | * T元素标识
75 | */
76 | private boolean isTElement;
77 |
78 | /**
79 | * 单元格数据类型,默认为字符串类型
80 | */
81 | private CellDataType nextDataType = CellDataType.SSTINDEX;
82 |
83 | private final DataFormatter sdf = new DataFormatter();
84 |
85 | private short formatIndex;
86 |
87 | private String formatString;
88 |
89 | // 定义前一个元素和当前元素的位置,用来计算其中空的单元格数量,如A6和A8等
90 | private String preRef = null;
91 |
92 | private String ref = null;
93 |
94 | // 定义该文档一行最大的单元格数,用来补全一行最后可能缺失的单元格
95 | private String maxRef = null;
96 |
97 | /**
98 | * 单元格
99 | */
100 | private StylesTable stylesTable;
101 |
102 | public XlsxHandler() {
103 | super();
104 | }
105 |
106 | public XlsxHandler(IHandlerContext context) {
107 | super();
108 | this.context = context;
109 | }
110 |
111 | public void setContext(IHandlerContext context) {
112 | this.context = context;
113 | }
114 |
115 | /**
116 | * 遍历工作簿中所有的电子表格
117 | *
118 | * @param filename
119 | * @throws IOException
120 | * @throws OpenXML4JException
121 | * @throws SAXException
122 | * @throws Exception
123 | */
124 | public void process(String filename) throws IOException, OpenXML4JException, SAXException {
125 | OPCPackage pkg = null;
126 | try {
127 | pkg = OPCPackage.open(filename);
128 | XSSFReader xssfReader = new XSSFReader(pkg);
129 | stylesTable = xssfReader.getStylesTable();
130 | SharedStringsTable sst = xssfReader.getSharedStringsTable();
131 | XMLReader parser = this.fetchSheetParser(sst);
132 | Iterator sheets = xssfReader.getSheetsData();
133 | while (sheets.hasNext()) {
134 | currRowIndex = 0;
135 | sheetIndex++;
136 | InputStream sheet = null;
137 | try {
138 | sheet = sheets.next();
139 | InputSource sheetSource = new InputSource(sheet);
140 | parser.parse(sheetSource);
141 | } finally {
142 | sheet.close();
143 | }
144 | }
145 | while (!context.fileEnd());
146 | } finally {
147 | pkg.close();
148 | }
149 | }
150 |
151 | private XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
152 | XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
153 | this.sharedStringsTable = sst;
154 | parser.setContentHandler(this);
155 | return parser;
156 | }
157 |
158 | @Override
159 | public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
160 | // c => 单元格
161 | if ("c".equals(name)) {
162 | // 前一个单元格的位置
163 | if (preRef == null) {
164 | preRef = attributes.getValue("r");
165 | } else {
166 | preRef = ref;
167 | }
168 | // 当前单元格的位置
169 | ref = attributes.getValue("r");
170 | // 设定单元格类型
171 | this.setNextDataType(attributes);
172 | // Figure out if the value is an index in the SST
173 | String cellType = attributes.getValue("t");
174 | if (cellType != null && cellType.equals("s")) {
175 | nextIsString = true;
176 | } else {
177 | nextIsString = false;
178 | }
179 | }
180 |
181 | // 当元素为t时
182 | if ("t".equals(name)) {
183 | isTElement = true;
184 | } else {
185 | isTElement = false;
186 | }
187 |
188 | // 置空
189 | lastContents.delete(0, lastContents.length());
190 | }
191 |
192 | /**
193 | * 单元格中的数据可能的数据类型
194 | */
195 | enum CellDataType {
196 | BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL
197 | }
198 |
199 | /**
200 | * 处理数据类型
201 | *
202 | * @param attributes
203 | */
204 | private void setNextDataType(Attributes attributes) {
205 | nextDataType = CellDataType.NUMBER;
206 | formatIndex = -1;
207 | formatString = null;
208 | String cellType = attributes.getValue("t");
209 | String cellStyleStr = attributes.getValue("s");
210 | String columData = attributes.getValue("r");
211 |
212 | if ("b".equals(cellType)) {
213 | nextDataType = CellDataType.BOOL;
214 | } else if ("e".equals(cellType)) {
215 | nextDataType = CellDataType.ERROR;
216 | } else if ("inlineStr".equals(cellType)) {
217 | nextDataType = CellDataType.INLINESTR;
218 | } else if ("s".equals(cellType)) {
219 | nextDataType = CellDataType.SSTINDEX;
220 | } else if ("str".equals(cellType)) {
221 | nextDataType = CellDataType.FORMULA;
222 | }
223 |
224 | if (cellStyleStr != null) {
225 | int styleIndex = Integer.parseInt(cellStyleStr);
226 | XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
227 | formatIndex = style.getDataFormat();
228 | formatString = style.getDataFormatString();
229 |
230 | if ("m/d/yy" == formatString) {
231 | nextDataType = CellDataType.DATE;
232 | formatString = "yyyy-MM-dd hh:mm:ss.SSS";
233 | }
234 |
235 | if (formatString == null) {
236 | nextDataType = CellDataType.NULL;
237 | formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
238 | }
239 | }
240 | }
241 |
242 | /**
243 | * 对解析出来的数据进行类型处理
244 | *
245 | * @param value
246 | * 单元格的值(这时候是一串数字)
247 | * @param thisStr
248 | * 一个空字符串
249 | * @return
250 | */
251 | @SuppressWarnings("deprecation")
252 | private String getDataValue(String value, String thisStr) {
253 | switch (nextDataType) {
254 | // 这几个的顺序不能随便交换,交换了很可能会导致数据错误
255 | case BOOL:
256 | char first = value.charAt(0);
257 | thisStr = first == '0' ? "FALSE" : "TRUE";
258 | break;
259 | case ERROR:
260 | thisStr = "\"ERROR:" + value.toString() + '"';
261 | break;
262 | case FORMULA:
263 | thisStr = '"' + value.toString() + '"';
264 | break;
265 | case INLINESTR:
266 | XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());
267 | thisStr = rtsi.toString();
268 | rtsi = null;
269 | break;
270 | case SSTINDEX:
271 | String sstIndex = value.toString();
272 | try {
273 | int idx = Integer.parseInt(sstIndex);
274 | XSSFRichTextString rtss = new XSSFRichTextString(sharedStringsTable.getEntryAt(idx));
275 | thisStr = rtss.toString();
276 | rtss = null;
277 | } catch (NumberFormatException ex) {
278 | thisStr = value.toString();
279 | }
280 | break;
281 | case NUMBER:
282 | if (formatString != null) {
283 | thisStr = sdf.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString).trim();
284 | } else {
285 | thisStr = value;
286 | }
287 |
288 | thisStr = thisStr.replace("_", "").trim();
289 | break;
290 | case DATE:
291 | thisStr = sdf.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString);
292 | // 对日期字符串作特殊处理
293 | thisStr = thisStr.replace(" ", "T");
294 | break;
295 | default:
296 | thisStr = " ";
297 | break;
298 | }
299 | return thisStr;
300 | }
301 |
302 | @Override
303 | public void endElement(String uri, String localName, String name) throws SAXException {
304 | // 根据SST的索引值的到单元格的真正要存储的字符串
305 | // 这时characters()方法可能会被调用多次
306 | if (nextIsString && StringUtils.isNotEmpty(lastContents) && StringUtils.isNumeric(lastContents)) {
307 | int idx = Integer.parseInt(lastContents.toString());
308 | lastContents.append(new XSSFRichTextString(sharedStringsTable.getEntryAt(idx)).toString());
309 | }
310 | // t元素也包含字符串
311 | if (isTElement) {
312 | // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
313 | String value = lastContents.toString().trim();
314 | currRowList.add(currColIndex, value);
315 | currColIndex++;
316 | isTElement = false;
317 | } else if ("v".equals(name)) {
318 | // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
319 | String value = this.getDataValue(lastContents.toString().trim(), "");
320 | // 补全单元格之间的空单元格
321 | if (!ref.equals(preRef)) {
322 | int len = countNullCell(ref, preRef);
323 | for (int i = 0; i < len; i++) {
324 | currRowList.add(currColIndex, "");
325 | currColIndex++;
326 | }
327 | }
328 | currRowList.add(currColIndex, value);
329 | currColIndex++;
330 | } else {
331 | // 如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法
332 | if (name.equals("row")) {
333 | // 默认第一行为表头,以该行单元格数目为最大数目
334 | if (currRowIndex == 0) {
335 | maxRef = ref;
336 | }
337 | // 补全一行尾部可能缺失的单元格
338 | if (maxRef != null) {
339 | int len = countNullCell(maxRef, ref);
340 | for (int i = 0; i <= len; i++) {
341 | currRowList.add(currColIndex, "");
342 | currColIndex++;
343 | }
344 | }
345 | try {
346 | context.newRow(currRowList);
347 | } catch (Exception e) {
348 | // do nothing
349 | }
350 | currRowList = Lists.newArrayListWithExpectedSize(currRowList.size());
351 | currRowIndex++;
352 | currColIndex = 0;
353 | preRef = null;
354 | ref = null;
355 | }
356 | }
357 | }
358 |
359 | /**
360 | * 计算两个单元格之间的单元格数目(同一行)
361 | *
362 | * @param ref
363 | * @param preRef
364 | * @return
365 | */
366 | private int countNullCell(String ref, String preRef) {
367 | // excel2007最大行数是1048576,最大列数是16384,最后一列列名是XFD
368 | String xfd = ref.replaceAll("\\d+", "");
369 | String xfd_1 = preRef.replaceAll("\\d+", "");
370 |
371 | xfd = fillChar(xfd, 3, '@', true);
372 | xfd_1 = fillChar(xfd_1, 3, '@', true);
373 |
374 | char[] letter = xfd.toCharArray();
375 | char[] letter_1 = xfd_1.toCharArray();
376 | int res = (letter[0] - letter_1[0]) * 26 * 26 + (letter[1] - letter_1[1]) * 26 + (letter[2] - letter_1[2]);
377 | return res - 1;
378 | }
379 |
380 | /**
381 | * 字符串的填充
382 | *
383 | * @param str
384 | * @param len
385 | * @param let
386 | * @param isPre
387 | * @return
388 | */
389 | private String fillChar(String str, int len, char let, boolean isPre) {
390 | int len_1 = str.length();
391 | if (len_1 < len) {
392 | if (isPre) {
393 | for (int i = 0; i < (len - len_1); i++) {
394 | str = let + str;
395 | }
396 | } else {
397 | for (int i = 0; i < (len - len_1); i++) {
398 | str = str + let;
399 | }
400 | }
401 | }
402 | return str;
403 | }
404 |
405 | @Override
406 | public void characters(char[] ch, int start, int length) throws SAXException {
407 | // 得到单元格内容的值
408 | lastContents.append(new String(ch, start, length));
409 | }
410 | }
--------------------------------------------------------------------------------
/src/test/java/com/github/Dorae132/easyutil/easyexcel/UtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.github.Dorae132.easyutil.easyexcel;
2 |
3 | import java.io.File;
4 | import java.util.List;
5 | import java.util.concurrent.TimeUnit;
6 | import java.util.concurrent.atomic.AtomicInteger;
7 |
8 | import org.junit.Test;
9 |
10 | import com.github.Dorae132.easyutil.easyexcel.common.Pair;
11 | import com.github.Dorae132.easyutil.easyexcel.export.DefaultWorkbookProcessor;
12 | import com.github.Dorae132.easyutil.easyexcel.export.ExcelCol;
13 | import com.github.Dorae132.easyutil.easyexcel.export.FillSheetModeEnums;
14 | import com.github.Dorae132.easyutil.easyexcel.export.IDataSupplier;
15 | import com.github.Dorae132.easyutil.easyexcel.read.IReadDoneCallBack;
16 | import com.github.Dorae132.easyutil.easyexcel.read.IRowConsumer;
17 | import com.google.common.collect.Lists;
18 |
19 | @SuppressWarnings("unchecked")
20 | public class UtilsTest {
21 |
22 | static class TestValue {
23 | @ExcelCol(title = "姓名")
24 | private String name;
25 | @ExcelCol(title = "年龄", order = 1)
26 | private String age;
27 | @ExcelCol(title = "学校", order = 3)
28 | private String school;
29 | @ExcelCol(title = "年级", order = 2)
30 | private String clazz;
31 |
32 | public TestValue(String name, String age, String school, String clazz) {
33 | super();
34 | this.name = name;
35 | this.age = age;
36 | this.school = school;
37 | this.clazz = clazz;
38 | }
39 | }
40 |
41 | private static List getData(int count) {
42 | List dataList = Lists.newArrayListWithCapacity(count);
43 | for (int i = 0; i < count; i++) {
44 | dataList.add(new TestValue("张三" + i, "age: " + i, null, "clazz: " + i));
45 | }
46 | return dataList;
47 | }
48 |
49 | // @Test
50 | // public void testCommonMode() throws Exception {
51 | // List dataList = getData(100000);
52 | // long start = System.currentTimeMillis();
53 | // ExcelProperties properties =
54 | // ExcelProperties.produceCommonProperties("", dataList,
55 | // "C:\\Users\\Dorae\\Desktop\\ttt\\", "common.xlsx", 0, null, 0, null);
56 | // File file = (File) ExcelUtils.excelExport(properties,
57 | // FillSheetModeEnums.COMMON_MODE.getValue());
58 | // System.out.println("commonMode: " + (System.currentTimeMillis() - start));
59 | // }
60 |
61 | @Test
62 | public static void testAppend() throws Exception {
63 | List dataList = getData(100000);
64 | long start = System.currentTimeMillis();
65 | ExcelProperties properties = ExcelProperties.produceAppendProperties("",
66 | "C:\\Users\\Dorae\\Desktop\\ttt\\", "append.xlsx", 0, TestValue.class, 0, new DefaultWorkbookProcessor(),
67 | new IDataSupplier() {
68 | private int i = 0;
69 |
70 | @Override
71 | public Pair, Boolean> getDatas() {
72 | boolean hasNext = i < 9;
73 | i++;
74 | return Pair.of(dataList, hasNext);
75 | }
76 | });
77 | File file = ExcelUtils.excelExport(properties, FillSheetModeEnums.PARALLEL_APPEND_MODE.getValue());
78 | System.out.println("apendMode: " + (System.currentTimeMillis() - start));
79 | }
80 |
81 | @Test
82 | public static void testRead() throws Exception {
83 | AtomicInteger count = new AtomicInteger(0);
84 | long start = System.currentTimeMillis();
85 | ExcelUtils.excelRead(ExcelProperties.produceReadProperties("C:\\Users\\Dorae\\Desktop\\ttt\\",
86 | "append_7f4ef636ff5e47e19504e17d963c4026.xlsx"), new IRowConsumer() {
87 | @Override
88 | public void consume(List row) {
89 | System.out.println(row);
90 | count.incrementAndGet();
91 | try {
92 | TimeUnit.MICROSECONDS.sleep(100);
93 | } catch (InterruptedException e) {
94 | // TODO Auto-generated catch block
95 | e.printStackTrace();
96 | }
97 | }
98 | }, new IReadDoneCallBack() {
99 | @Override
100 | public Void call() {
101 | System.out.println(
102 | "end, count: " + count.get() + "\ntime: " + (System.currentTimeMillis() - start));
103 | return null;
104 | }
105 | }, 3, true);
106 | System.out.println("main end" + count.get());
107 | }
108 |
109 | public static void main(String[] args) throws Exception {
110 | // testAppend();
111 | testRead();
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------