Repository: wuwz/ExcelKit Branch: master Commit: ccd3e660c19c Files: 34 Total size: 94.0 KB Directory structure: gitextract_lm2w90xj/ ├── .gitignore ├── README.md ├── pom.xml └── src/ └── main/ └── java/ └── com/ └── wuwenze/ └── poi/ ├── ExcelKit.java ├── annotation/ │ ├── Excel.java │ └── ExcelField.java ├── config/ │ └── Options.java ├── convert/ │ ├── ReadConverter.java │ └── WriteConverter.java ├── exception/ │ ├── ExcelKitAnnotationAnalyzeException.java │ ├── ExcelKitConfigAnalyzeFailureException.java │ ├── ExcelKitConfigFileNotFoundException.java │ ├── ExcelKitEncounterNoNeedXmlException.java │ ├── ExcelKitReadConverterException.java │ ├── ExcelKitRuntimeException.java │ ├── ExcelKitWriteConverterException.java │ └── ExcelKitXmlAnalyzeException.java ├── factory/ │ └── ExcelMappingFactory.java ├── handler/ │ └── ExcelReadHandler.java ├── pojo/ │ ├── ExcelErrorField.java │ ├── ExcelMapping.java │ └── ExcelProperty.java ├── util/ │ ├── BeanUtil.java │ ├── Const.java │ ├── DateUtil.java │ ├── POIUtil.java │ ├── PathUtil.java │ ├── RegexUtil.java │ └── ValidatorUtil.java ├── validator/ │ ├── EmailValidator.java │ ├── MobileValidator.java │ └── Validator.java └── xlsx/ ├── ExcelXlsxReader.java └── ExcelXlsxWriter.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ #eclipse files .classpath .project /bin/ /target/ /.settings/ #idea files target/ *.iml *.ipr *.iws .idea/ ================================================ FILE: README.md ================================================ # ExcelKit > 简单、好用且轻量级的海量Excel文件导入导出解决方案。 # 注意 ## 停止维护,建议使用EasyExcel:https://github.com/alibaba/easyexcel ## POM.xml ```xml com.wuwenze ExcelKit 2.0.72 ``` ## 示例 ### 1. ExcelMapping (配置Excel与实体之间的映射关系) > 现支持两种配置方式: 注解 或者 XML ```java @Excel("用户信息") public class User { @ExcelField(value = "编号", width = 30) private Integer userId; @ExcelField(// value = "用户名",// required = true,// validator = UsernameValidator.class,// comment = "请填写用户名,最大长度为12,且不能重复" ) private String username; @ExcelField(value = "密码", required = true, maxLength = 32) private String password; @ExcelField(value = "邮箱", validator = UserEmailValidator.class) private String email; @ExcelField(// value = "性别",// readConverterExp = "未知=0,男=1,女=2",// writeConverterExp = "0=未知,1=男,2=女",// options = SexOptions.class// ) private Integer sex; @ExcelField(// value = "用户组",// name = "userGroup.name",// options = UserGroupNameOptions.class ) private UserGroup userGroup; @ExcelField(value = "创建时间", dateFormat = "yyyy/MM/dd HH:mm:ss") private Date createAt; @ExcelField(// value = "自定义字段",// maxLength = 80,// comment = "可以乱填,但是长度不能超过80,导入时最终会转换为数字",// writeConverter = CustomizeFieldWriteConverter.class,// 写文件时,将数字转字符串 readConverter = CustomizeFieldReadConverter.class// 读文件时,将字符串转数字 ) private Integer customizeField; // Getter and Setter .. } ``` > XML 配置方式, 必须需将 xml 文件放置在`classpath:excel-mapping/{entityClassName}.xml` ```java public class User2 { private Integer userId; private String username; private String password; private String email; private Integer sex; private UserGroup userGroup; private Date createAt; private Integer customizeField; // Getter and Setter .. } ``` > classpath:excel-mapping/com.wuwenze.entity.User2.xml ```xml ``` 以上两种方式2选一即可, ExcelKit 会`优先装载 XML 配置文件`. ### 2. 实现相关的转换器 > 通过实现`com.wuwenze.poi.config.Options`自定义导入模板的下拉框数据源。 ```java public class UserGroupNameOptions implements Options { @Override public String[] get() { return new String[]{"管理组", "普通会员组", "游客"}; } } ``` > 通过实现`com.wuwenze.poi.validator.Validator`自定义单元格导入时的验证规则。 ```java public class UsernameValidator implements Validator { final List ERROR_USERNAME_LIST = Lists.newArrayList( "admin", "root", "master", "administrator", "sb" ); @Override public String valid(Object o) { String username = (String) o; if (username.length() > 12) { return "用户名不能超过12个字符。"; } if (ERROR_USERNAME_LIST.contains(username)) { return "用户名非法,不允许使用。"; } return null; } } ``` > 实现`com.wuwenze.poi.convert.WriteConverter`以及`com.wuwenze.poi.convert.ReadConverter`单元格读写转换器。 ```java public class CustomizeFieldWriteConverter implements WriteConverter { /** * 写文件时,将值进行转换(此处示例为将数值拼接为指定格式的字符串) */ @Override public String convert(Object o) throws ExcelKitWriteConverterException { return (o + "_convertedValue"); } } public class CustomizeFieldReadConverter implements ReadConverter { /** * 读取单元格时,将值进行转换(此处示例为计算单元格字符串char的总和) */ @Override public Object convert(Object o) throws ExcelKitReadConverterException { String value = (String) o; int convertedValue = 0; for (char c : value.toCharArray()) { convertedValue += Integer.valueOf(c); } return convertedValue; } } ``` ### 3. 一行代码构建 Excel 导入模板 > 使用 ExcelKit 提供的API 构建导入模板, 会根据配置生成批注, 下拉框等 ``` java // 生成导入模板(含3条示例数据) @RequestMapping(value = "/downTemplate", method = RequestMethod.GET) public void downTemplate(HttpServletResponse response) { List userList = DbUtil.getUserList(3); ExcelKit.$Export(User.class, response).downXlsx(userList, true); } ``` ![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539800515-e9714f25-e415-4e70-a4b9-2b5a229dfce0.png) ![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539833934-6b7b2ca8-c7a0-4872-a1a9-722fc5c403d1.png) ### 4. 执行文件导入 > 使用边读边处理的方式, 无需担心内存溢出, 也不用理会 Excel 文件到底有多大. ``` java @RequestMapping(value = "/importUser", method = RequestMethod.POST) public ResponseEntity importUser(@RequestParam MultipartFile file) throws IOException { long beginMillis = System.currentTimeMillis(); List successList = Lists.newArrayList(); List> errorList = Lists.newArrayList(); ExcelKit.$Import(User.class) .readXlsx(file.getInputStream(), new ExcelReadHandler() { @Override public void onSuccess(int sheetIndex, int rowIndex, User entity) { successList.add(entity); // 单行读取成功,加入入库队列。 } @Override public void onError(int sheetIndex, int rowIndex, List errorFields) { // 读取数据失败,记录了当前行所有失败的数据 errorList.add(MapUtil.newHashMap(// "sheetIndex", sheetIndex,// "rowIndex", rowIndex,// "errorFields", errorFields// )); } }); // TODO: 执行successList的入库操作。 return ResponseEntity.ok(MapUtil.newHashMap( "data", successList, "haveError", !CollectionUtil.isEmpty(errorList), "error", errorList, "timeConsuming", (System.currentTimeMillis() - beginMillis) / 1000L )); } ``` > 全部导入成功示例: ![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539857806-7dd5b511-4cfe-43f7-8d82-f76bd831e7af.png) > 部分导入失败示例(包含错误信息): ![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539878743-a0cc24a2-1f30-4ccc-8d14-225a3bad30f5.png) ### 5. 一行代码执行 Excel 批量导出. > 基于 Apache POI SXSSF 系列API实现导出, 大幅优化导出性能. ``` java @RequestMapping(value = "/downXlsx", method = RequestMethod.GET) public void downXlsx(HttpServletResponse response) { long beginMillis = System.currentTimeMillis(); List userList = DbUtil.getUserList(100000);// 生成10w条测试数据 ExcelKit.$Export(User.class, response).downXlsx(userList, false); log.info("#ExcelKit.$Export success, size={},timeConsuming={}s",// userList.size(), (System.currentTimeMillis() - beginMillis) / 1000L); } ``` ![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539922063-7681b3df-6ccf-4f42-939b-269c84b8f8bc.png) 需要注意的是,虽然ExcelKit针对导出做了大量优化,但导出数据也需要量力而行。 ![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539967120-9eff43bf-d225-4268-8685-2b441f7024d5.png) ================================================ FILE: pom.xml ================================================ 4.0.0 com.wuwenze ExcelKit 2.0.72 jar ExcelKit http://gitee.com/wuwenze/ExcelKit Excel导入导出工具(简单、好用且轻量级的海量Excel文件导入导出解决方案.) wuwenze https://wuwenze.com wenzewoo@gmail.com The Apache Software License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt scm:git:git@gitee.com:wuwenze/ExcelKit.git scm:git:git@gitee.com:wuwenze/ExcelKit.git git@gitee.com:wuwenze/ExcelKit.git UTF-8 1.6 3.17 1.6.1 1.1.6 2.11.0 18.0 org.apache.poi poi-ooxml ${poi-version} org.apache.poi poi-ooxml-schemas ${poi-version} dom4j dom4j ${dom4j-version} jaxen jaxen ${jaxen-version} xerces xercesImpl ${xerces-version} xml-apis xml-apis 2.0.2 com.google.guava guava 18.0 org.projectlombok lombok 1.16.10 provided javax.servlet servlet-api 2.5 provided commons-beanutils commons-beanutils 1.9.3 junit junit 4.12 test org.apache.maven.plugins maven-compiler-plugin ${jdk-version} ${jdk-version} ${encoding} jdk-profile true ${jdk-version} ${jdk-version} ${jdk-version} ${jdk-version} release org.apache.maven.plugins maven-source-plugin 2.2.1 package jar-no-fork org.apache.maven.plugins maven-javadoc-plugin 2.9.1 package jar org.apache.maven.plugins maven-gpg-plugin 1.5 verify sign sonatype https://oss.sonatype.org/content/repositories/snapshots/ sonatype https://oss.sonatype.org/service/local/staging/deploy/maven2/ ================================================ FILE: src/main/java/com/wuwenze/poi/ExcelKit.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi; import com.wuwenze.poi.exception.ExcelKitRuntimeException; import com.wuwenze.poi.factory.ExcelMappingFactory; import com.wuwenze.poi.handler.ExcelReadHandler; import com.wuwenze.poi.pojo.ExcelMapping; import com.wuwenze.poi.util.Const; import com.wuwenze.poi.util.POIUtil; import com.wuwenze.poi.xlsx.ExcelXlsxReader; import com.wuwenze.poi.xlsx.ExcelXlsxWriter; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; import java.util.List; import javax.servlet.http.HttpServletResponse; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.poi.xssf.streaming.SXSSFWorkbook; /** * @author wuwenze */ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ExcelKit { private Class mClass = null; private HttpServletResponse mResponse = null; private OutputStream mOutputStream = null; private Integer mMaxSheetRecords = 50000; private String mCurrentOptionMode = ExcelKit.MODE_EXPORT; private final static String MODE_EXPORT = "$MODE_EXPORT$"; private final static String MODE_BUILD = "$MODE_BUILD$"; private final static String MODE_IMPORT = "$MODE_IMPORT$"; /** * 使用此构造器来执行浏览器导出 * * @param clazz 导出实体对象 * @param response 原生 response 对象, 用于响应浏览器下载 * @return ExcelKit obj. * @see ExcelKit#downXlsx(List, boolean) */ public static ExcelKit $Export(Class clazz, HttpServletResponse response) { return new ExcelKit(clazz, response); } public void downXlsx(List data, boolean isTemplate) { if (!mCurrentOptionMode.equals(ExcelKit.MODE_EXPORT)) { throw new ExcelKitRuntimeException( "请使用com.wuwenze.poi.ExcelKit.$Export(Class clazz, HttpServletResponse response)构造器初始化参数."); } try { ExcelMapping excelMapping = ExcelMappingFactory.get(mClass); ExcelXlsxWriter excelXlsxWriter = new ExcelXlsxWriter(excelMapping, mMaxSheetRecords); SXSSFWorkbook workbook = excelXlsxWriter.generateXlsxWorkbook(data, isTemplate); String fileName = isTemplate ? (excelMapping.getName() + "-导入模板.xlsx") : (excelMapping.getName() + "-导出结果.xlsx"); POIUtil.download(workbook, mResponse, URLEncoder.encode(fileName, Const.ENCODING)); } catch (Throwable e) { throw new ExcelKitRuntimeException("downXlsx error", e); } } /** * 使用此构造器来执行构建文件流. * * @param clazz 导出实体对象 * @param outputStream 输出流 * @return ExcelKit obj. * @see ExcelKit#writeXlsx(List, boolean) */ public static ExcelKit $Builder(Class clazz, OutputStream outputStream) { return new ExcelKit(clazz, outputStream); } public void writeXlsx(List data, boolean isTemplate) { if (!mCurrentOptionMode.equals(ExcelKit.MODE_BUILD)) { throw new ExcelKitRuntimeException( "请使用com.wuwenze.poi.ExcelKit.$Builder(Class clazz, OutputStream outputStream)构造器初始化参数."); } ExcelMapping excelMapping = ExcelMappingFactory.get(mClass); ExcelXlsxWriter excelXlsxWriter = new ExcelXlsxWriter(excelMapping, mMaxSheetRecords); SXSSFWorkbook workbook = excelXlsxWriter.generateXlsxWorkbook(data, isTemplate); POIUtil.write(workbook, mOutputStream); } /** * 使用此构造器来执行Excel文件导入. * * @param clazz 导出实体对象 * @return ExcelKit obj. * @see ExcelKit#readXlsx(File, Integer, ExcelReadHandler) * @see ExcelKit#readXlsx(InputStream, Integer, ExcelReadHandler) * @see ExcelKit#readXlsx(File, ExcelReadHandler) * @see ExcelKit#readXlsx(InputStream, ExcelReadHandler) */ public static ExcelKit $Import(Class clazz) { return new ExcelKit(clazz); } public void readXlsx(File excelFile, ExcelReadHandler excelReadHandler) { readXlsx(excelFile, -1, excelReadHandler); } public void readXlsx(File excelFile, Integer sheetIndex, ExcelReadHandler excelReadHandler) { try { InputStream inputStream = new FileInputStream(excelFile); readXlsx(inputStream, sheetIndex, excelReadHandler); } catch (Throwable e) { throw new ExcelKitRuntimeException("readXlsx error", e); } } public void readXlsx(InputStream inputStream, ExcelReadHandler excelReadHandler) { readXlsx(inputStream, -1, excelReadHandler); } public void readXlsx(InputStream inputStream, Integer sheetIndex, ExcelReadHandler excelReadHandler) { if (!mCurrentOptionMode.equals(ExcelKit.MODE_IMPORT)) { throw new ExcelKitRuntimeException( "请使用com.wuwenze.poi.ExcelKit.$Import(Class clazz)构造器初始化参数."); } ExcelMapping excelMapping = ExcelMappingFactory.get(mClass); ExcelXlsxReader excelXlsxReader = new ExcelXlsxReader(mClass, excelMapping, excelReadHandler); if (sheetIndex >= 0) { excelXlsxReader.process(inputStream, sheetIndex); return; } excelXlsxReader.process(inputStream); } public ExcelKit setMaxSheetRecords(Integer mMaxSheetRecords) { this.mMaxSheetRecords = mMaxSheetRecords; return this; } protected ExcelKit(Class clazz) { this(clazz, null, null); mCurrentOptionMode = ExcelKit.MODE_IMPORT; } protected ExcelKit(Class clazz, OutputStream outputStream) { this(clazz, outputStream, null); mCurrentOptionMode = ExcelKit.MODE_BUILD; } protected ExcelKit(Class clazz, HttpServletResponse response) { this(clazz, null, response); mCurrentOptionMode = ExcelKit.MODE_EXPORT; } protected ExcelKit( Class clazz, OutputStream outputStream, HttpServletResponse response) { mClass = clazz; mOutputStream = outputStream; mResponse = response; } } ================================================ FILE: src/main/java/com/wuwenze/poi/annotation/Excel.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author wuwenze */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Excel { String value() default ""; } ================================================ FILE: src/main/java/com/wuwenze/poi/annotation/ExcelField.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.annotation; import com.wuwenze.poi.config.Options; import com.wuwenze.poi.convert.ReadConverter; import com.wuwenze.poi.convert.WriteConverter; import com.wuwenze.poi.validator.Validator; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author wuwenze */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface ExcelField { /** * @return 单元格名称(如 : id字段显示为 ' 编号 ') 默认为字段名 */ String value() default ""; /** * 属性名, 仅在复杂数据类型时配置. *
   *   (At)ExcelField(name="user.name");
   *   private User user;
   * 
* * @return 属性名 */ String name() default ""; /** * @return 单元格宽度[仅限表头] 默认-1(自动计算列宽) */ short width() default -1; /** * @return 是否必填 */ boolean required() default false; /** * @return 批注信息, 生成模板时生效 */ String comment() default ""; /** * @return 最大长度, 读取时生效, 默认不限制 */ int maxLength() default -1; /** * 日期格式, 如: yyyy/MM/dd * * @return 日期格式 */ String dateFormat() default ""; /** * @return 下拉框数据源, 生成模板和验证数据时生效 */ Class options() default Void.class; /** * 写入内容转换表达式 (如: 1=男,2=女), 与 writeConverter 二选一(优先级0) * * @return 写入内容转换表达式 * @see ExcelField#writeConverter() */ String writeConverterExp() default ""; /** * 写入内容转换器, 与 writeConverterExp 二选一(优先级1) * * @return 写入内容转换器 * @see ExcelField#writeConverterExp() */ Class writeConverter() default Void.class; /** * 读取内容转表达式 (如: 男=1,女=2), 与 readConverter 二选一(优先级0) * * @return 读取内容转表达式 * @see ExcelField#readConverter() */ String readConverterExp() default ""; /** * 读取内容转换器, 与 readConverterExp 二选一(优先级1) * * @return 读取内容转换器 * @see ExcelField#readConverterExp() */ Class readConverter() default Void.class; /** * 正则表达式, 读取时生效, 与 validator 二选一(优先级0) * * @return 正则表达式 * @see ExcelField#validator() */ String regularExp() default ""; /** * 正则表达式验证失败时的错误消息, regularExp 配置后生效 * * @return 正则表达式验证失败时的错误消息 * @see ExcelField#regularExp() */ String regularExpMessage() default ""; /** * 自定义验证器, 读取时生效, 与 regularExp 二选一(优先级1) * * @return 自定义验证器 * @see ExcelField#regularExp() */ Class validator() default Void.class; class Void implements Options, ReadConverter, WriteConverter, Validator { @Override public String[] get() { return new String[0]; } @Override public String convert(Object value) { return null; } @Override public String valid(Object value) { return null; } } } ================================================ FILE: src/main/java/com/wuwenze/poi/config/Options.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.config; /** * @author wuwenze */ public interface Options { /** * 指定excel单元格的下拉框数据源, 用于规范生成Excel模板的数据校验 * * @return ["option1", "option2"] */ String[] get(); } ================================================ FILE: src/main/java/com/wuwenze/poi/convert/ReadConverter.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.convert; import com.wuwenze.poi.exception.ExcelKitReadConverterException; /** * @author wuwenze */ public interface ReadConverter { /** * 将value转换成指定的值, 读取时映射到实体中 * * @param value 当前单元格的值 * @return 转换后的值 */ Object convert(Object value) throws ExcelKitReadConverterException; } ================================================ FILE: src/main/java/com/wuwenze/poi/convert/WriteConverter.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.convert; import com.wuwenze.poi.exception.ExcelKitWriteConverterException; /** * @author wuwenze */ public interface WriteConverter { /** * 将value转换成指定的值, 用于写入excel表格中 * * @param value 当前单元格的值 * @return 转换后的值 */ String convert(Object value) throws ExcelKitWriteConverterException; } ================================================ FILE: src/main/java/com/wuwenze/poi/exception/ExcelKitAnnotationAnalyzeException.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.exception; /** * @author wuwenze */ public class ExcelKitAnnotationAnalyzeException extends ExcelKitRuntimeException { private static final long serialVersionUID = -7412071094510468644L; public ExcelKitAnnotationAnalyzeException(String message) { super(message); } } ================================================ FILE: src/main/java/com/wuwenze/poi/exception/ExcelKitConfigAnalyzeFailureException.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.exception; /** * @author wuwenze */ public class ExcelKitConfigAnalyzeFailureException extends ExcelKitRuntimeException { private static final long serialVersionUID = -6037535579660494996L; public ExcelKitConfigAnalyzeFailureException(String message) { super(message); } public ExcelKitConfigAnalyzeFailureException(Throwable cause) { super(cause); } } ================================================ FILE: src/main/java/com/wuwenze/poi/exception/ExcelKitConfigFileNotFoundException.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.exception; /** * @author wuwenze */ public class ExcelKitConfigFileNotFoundException extends ExcelKitRuntimeException { private static final long serialVersionUID = -5061679627805728661L; public ExcelKitConfigFileNotFoundException(String message) { super(message); } } ================================================ FILE: src/main/java/com/wuwenze/poi/exception/ExcelKitEncounterNoNeedXmlException.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.exception; /** * @author wuwenze */ public class ExcelKitEncounterNoNeedXmlException extends ExcelKitRuntimeException { private static final long serialVersionUID = 4096057792690617204L; } ================================================ FILE: src/main/java/com/wuwenze/poi/exception/ExcelKitReadConverterException.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.exception; /** * @author wuwenze */ public class ExcelKitReadConverterException extends ExcelKitRuntimeException { private static final long serialVersionUID = 2832313288988433562L; public ExcelKitReadConverterException(String message) { super(message); } } ================================================ FILE: src/main/java/com/wuwenze/poi/exception/ExcelKitRuntimeException.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.exception; /** * @author wuwenze */ public class ExcelKitRuntimeException extends RuntimeException { private static final long serialVersionUID = 1059413765208343152L; public ExcelKitRuntimeException() { } public ExcelKitRuntimeException(String message) { super(message); } public ExcelKitRuntimeException(String message, Throwable cause) { super(message, cause); } public ExcelKitRuntimeException(Throwable cause) { super(cause); } } ================================================ FILE: src/main/java/com/wuwenze/poi/exception/ExcelKitWriteConverterException.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.exception; /** * @author wuwenze */ public class ExcelKitWriteConverterException extends ExcelKitRuntimeException { private static final long serialVersionUID = 7989007279778794434L; public ExcelKitWriteConverterException(String message) { super(message); } } ================================================ FILE: src/main/java/com/wuwenze/poi/exception/ExcelKitXmlAnalyzeException.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.exception; /** * @author wuwenze */ public class ExcelKitXmlAnalyzeException extends ExcelKitRuntimeException { private static final long serialVersionUID = 2433077029852196265L; public ExcelKitXmlAnalyzeException(String message) { super(message); } } ================================================ FILE: src/main/java/com/wuwenze/poi/factory/ExcelMappingFactory.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.factory; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Lists; import com.wuwenze.poi.annotation.Excel; import com.wuwenze.poi.annotation.ExcelField; import com.wuwenze.poi.exception.ExcelKitAnnotationAnalyzeException; import com.wuwenze.poi.exception.ExcelKitConfigAnalyzeFailureException; import com.wuwenze.poi.exception.ExcelKitConfigFileNotFoundException; import com.wuwenze.poi.exception.ExcelKitXmlAnalyzeException; import com.wuwenze.poi.pojo.ExcelMapping; import com.wuwenze.poi.pojo.ExcelProperty; import com.wuwenze.poi.util.BeanUtil; import com.wuwenze.poi.util.PathUtil; import com.wuwenze.poi.util.ValidatorUtil; import java.io.File; import java.lang.reflect.Field; import java.util.Iterator; import java.util.List; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * @author wuwenze */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ExcelMappingFactory { private final static LoadingCache, ExcelMapping> mExcelMappingLoadingCache = CacheBuilder.newBuilder() .maximumSize(100) .build(new CacheLoader, ExcelMapping>() { @Override public ExcelMapping load(Class key) { return ExcelMappingFactory.loadExcelMappingByClass(key); } }); private final static List mClazzFields = Lists .newArrayList("options", "writeConverter", "readConverter", "validator"); private final static List mRequeridAttrs = Lists.newArrayList("name"); /** * 获取指定实体的Excel映射信息 * * @param clazz 实体 * @return ExcelMapping映射对象 */ public static ExcelMapping get(Class clazz) { try { return ExcelMappingFactory.mExcelMappingLoadingCache.get(clazz); } catch (Exception e) { throw new ExcelKitConfigAnalyzeFailureException(e); } } private static ExcelMapping loadExcelMappingByClass(Class clazz) { // 1. 从配置文件加载 (classpath:excel-mapping/className.xml) ExcelMapping excelMapping = null; boolean xmlConfigFileNotFound = false; String loadExcelMappingFailedMessage = null; try { excelMapping = ExcelMappingFactory.loadExcelMappingByXml(clazz.getName()); } catch (Exception e) { xmlConfigFileNotFound = e instanceof ExcelKitConfigFileNotFoundException; loadExcelMappingFailedMessage = e.getMessage(); } // 2. 从注解加载配置信息 (当配置文件未找到时) if (null == excelMapping && xmlConfigFileNotFound) { try { excelMapping = ExcelMappingFactory.loadExcelMappingByAnnotation(clazz); } catch (Exception e) { loadExcelMappingFailedMessage = e.getMessage(); } } // 3. 加载配置信息失败. if (null == excelMapping && null != loadExcelMappingFailedMessage) { throw new ExcelKitConfigAnalyzeFailureException(loadExcelMappingFailedMessage); } return excelMapping; } private static ExcelMapping loadExcelMappingByAnnotation(Class clazz) throws IllegalAccessException, InstantiationException { ExcelMapping excelMapping = new ExcelMapping(); Excel excel = clazz.getAnnotation(Excel.class); if (null == excel) { throw new ExcelKitAnnotationAnalyzeException( "[" + clazz.getName() + "] @Excel annotations not found."); } excelMapping.setName(excel.value()); ExcelProperty excelMappingProperty; Field[] fields = clazz.getDeclaredFields(); List propertyList = Lists.newArrayList(); for (Field field : fields) { ExcelField excelField = field.getAnnotation(ExcelField.class); if (null != excelField) { Class emptyClazz = ExcelField.Void.class; excelMappingProperty = ExcelProperty.builder() .name(ValidatorUtil.isEmpty(excelField.name()) ? field.getName() : excelField.name()) .required(excelField.required()) .column( ValidatorUtil.isEmpty(excelField.value()) ? field.getName() : excelField.value()) .comment(excelField.comment()) .maxLength(excelField.maxLength()) .width(excelField.width()) .dateFormat(excelField.dateFormat()) .options(excelField.options() != emptyClazz ? excelField.options().newInstance() : null) .writeConverterExp(excelField.writeConverterExp()) .writeConverter(excelField.writeConverter() != emptyClazz ? excelField.writeConverter() .newInstance() : null) .readConverterExp(excelField.readConverterExp()) .readConverter( excelField.readConverter() != emptyClazz ? excelField.readConverter().newInstance() : null) .regularExp(excelField.regularExp()) .regularExpMessage(excelField.regularExpMessage()) .validator( excelField.validator() != emptyClazz ? excelField.validator().newInstance() : null) .build(); propertyList.add(excelMappingProperty); } } if (propertyList.isEmpty()) { throw new ExcelKitAnnotationAnalyzeException( "[" + clazz.getName() + "] @ExcelField annotations not found."); } excelMapping.setPropertyList(propertyList); return excelMapping; } private static ExcelMapping loadExcelMappingByXml(String clazzName) throws Exception { ExcelMapping excelMapping = new ExcelMapping(); File config = PathUtil .getFileByClasspath(String.format("excel-mapping/%s.xml", clazzName)); String configFile = "classpath:excel-mapping/" + config.getName(); if (!config.exists()) { throw new ExcelKitConfigFileNotFoundException( "[" + configFile + "] not found."); } SAXReader reader = new SAXReader(); Document document = reader.read(config); Element rootElement = document.getRootElement(); if (!"excel-mapping".equals(rootElement.getName())) { throw new ExcelKitXmlAnalyzeException( "[" + configFile + "] not found."); } Attribute nameAttr = rootElement.attribute("name"); if (null == nameAttr) { throw new ExcelKitXmlAnalyzeException( "[" + configFile + "] attribute \"name\" not found."); } excelMapping.setName(nameAttr.getValue()); List propertyList = Lists.newArrayList(); Iterator elementIterator = rootElement.elementIterator(); while (elementIterator.hasNext()) { Element element = elementIterator.next(); if ("property".equals(element.getName())) { List attributes = element.attributes(); ExcelMappingFactory.checkXmlPropertyRequiredAttr(configFile, attributes); ExcelProperty excelMappingProperty = null; for (Attribute attribute : attributes) { if (null == excelMappingProperty) { excelMappingProperty = new ExcelProperty(); } String name = attribute.getName(); String value = attribute.getValue(); BeanUtil.setComplexProperty(excelMappingProperty, name, ExcelMappingFactory.validAndGetPropertyValue(configFile, name, value)); } if (null != excelMappingProperty) { propertyList.add(excelMappingProperty); } } } if (propertyList.isEmpty()) { throw new ExcelKitXmlAnalyzeException( "[" + configFile + "] not found."); } excelMapping.setPropertyList(propertyList); return excelMapping; } private static void checkXmlPropertyRequiredAttr(String configFile, List attributes) { Integer containsCount = 0; for (Attribute attr : attributes) { if (ExcelMappingFactory.mRequeridAttrs.contains(attr.getName())) { containsCount++; } } if (containsCount != ExcelMappingFactory.mRequeridAttrs.size()) { throw new ExcelKitXmlAnalyzeException( "[" + configFile + "] missing required attributes: " + ExcelMappingFactory.mRequeridAttrs .toString()); } } private static Object validAndGetPropertyValue( String configFile, String name, String value) { String messageTemplate = String .format("[%s] Analyze failed: ", configFile, name, value); if (ExcelMappingFactory.mClazzFields.contains(name)) { try { return Class.forName(value).newInstance(); } catch (Exception e) { throw new ExcelKitXmlAnalyzeException(messageTemplate + e.getMessage()); } } if ("writeConverterExp".equals(name) || "readConverterExp".equals(name)) { for (String item : value.split(",")) { if (!item.contains("=")) { throw new ExcelKitXmlAnalyzeException(messageTemplate + "Converter Expression error, Reference:[\"1=男,2=女\" or \"男=1,女=2\"]."); } } } return value; } } ================================================ FILE: src/main/java/com/wuwenze/poi/handler/ExcelReadHandler.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.handler; import com.wuwenze.poi.pojo.ExcelErrorField; import java.util.List; /** * @author wuwenze */ public interface ExcelReadHandler { void onSuccess(int sheetIndex, int rowIndex, T entity); void onError(int sheetIndex, int rowIndex, List errorFields); } ================================================ FILE: src/main/java/com/wuwenze/poi/pojo/ExcelErrorField.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.pojo; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; /** * @author wuwenze */ @Data @Builder @ToString @NoArgsConstructor @AllArgsConstructor public class ExcelErrorField { private Integer cellIndex; private String name; private String column; private String errorMessage; } ================================================ FILE: src/main/java/com/wuwenze/poi/pojo/ExcelMapping.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.pojo; import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; /** * @author wuwenze */ @Data @Builder @ToString @NoArgsConstructor @AllArgsConstructor public class ExcelMapping { private String name; private List propertyList; } ================================================ FILE: src/main/java/com/wuwenze/poi/pojo/ExcelProperty.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.pojo; import com.wuwenze.poi.config.Options; import com.wuwenze.poi.convert.ReadConverter; import com.wuwenze.poi.convert.WriteConverter; import com.wuwenze.poi.validator.Validator; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; /** * @author wuwenze */ @Data @Builder @ToString @NoArgsConstructor @AllArgsConstructor public class ExcelProperty { private String name; private String column; private Boolean required; private Short width; private String comment; private Integer maxLength; private String dateFormat; private Options options; private String writeConverterExp; private WriteConverter writeConverter; private String readConverterExp; private ReadConverter readConverter; private String regularExp; private String regularExpMessage; private Validator validator; } ================================================ FILE: src/main/java/com/wuwenze/poi/util/BeanUtil.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.util; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.PropertyUtilsBean; import org.apache.commons.beanutils.converters.DateConverter; import java.lang.reflect.InvocationTargetException; /** * @author wuwenze */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class BeanUtil extends org.apache.commons.beanutils.BeanUtils { public static void setComplexProperty(Object bean, String name, Object value) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException { //修复日期为空时bug ConvertUtils.register(new DateConverter(null), java.util.Date.class); if (!name.contains(".")) { BeanUtil.setProperty(bean, name, value); return; } PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean(); String[] propertyLevels = name.split("\\."); String parentPropertyName = ""; for (int i = 0; i < propertyLevels.length; i++) { String p = propertyLevels[i]; parentPropertyName = (parentPropertyName.equals("") ? p : parentPropertyName + "." + p); if (i < (propertyLevels.length - 1) && propertyUtilsBean.getProperty(bean, parentPropertyName) != null) { continue; } Class parentClass = propertyUtilsBean.getPropertyType(bean, parentPropertyName); if (i < (propertyLevels.length - 1)) { BeanUtil.setProperty(bean, parentPropertyName, parentClass.getConstructor().newInstance()); } else { BeanUtil.setProperty(bean, parentPropertyName, parentClass.getConstructor(String.class).newInstance(value)); } } } } ================================================ FILE: src/main/java/com/wuwenze/poi/util/Const.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.util; import lombok.AccessLevel; import lombok.NoArgsConstructor; /** * @author wuwenze */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class Const { public static final String ENCODING = "UTF-8"; public static final String XLSX_SUFFIX = ".xlsx"; public static final String XLSX_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; public static final String XLSX_HEADER_KEY = "Content-disposition"; public static final String XLSX_HEADER_VALUE_TEMPLATE = "attachment; filename=%s"; public static final String XLSX_DEFAULT_EMPTY_CELL_VALUE = "$EMPTY_CELL$"; public static final Integer XLSX_DEFAULT_BEGIN_READ_ROW_INDEX = 1; public static final String SAX_PARSER_CLASS = "org.apache.xerces.parsers.SAXParser"; public static final String SAX_C_ELEMENT = "c"; public static final String SAX_R_ATTR = "r"; public static final String SAX_T_ELEMENT = "t"; public static final String SAX_S_ATTR_VALUE = "s"; public static final String SAX_RID_PREFIX = "rId"; public static final String SAX_ROW_ELEMENT = "row"; } ================================================ FILE: src/main/java/com/wuwenze/poi/util/DateUtil.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.util; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.concurrent.ExecutionException; import lombok.AccessLevel; import lombok.NoArgsConstructor; /** * @author wuwenze */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class DateUtil { public final static SimpleDateFormat ENGLISH_LOCAL_DF = new SimpleDateFormat( "EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH); private final static LoadingCache mDateFormatLoadingCache = CacheBuilder.newBuilder() .maximumSize(5) .build(new CacheLoader() { @Override public SimpleDateFormat load(String pattern) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern); simpleDateFormat.setLenient(true); return simpleDateFormat; } }); public static Date parse(String pattern, Object value) throws Exception { String valueString = (String) value; return DateUtil.mDateFormatLoadingCache.get(pattern).parse(valueString); } public static String format(String pattern, Date value) { try { return DateUtil.mDateFormatLoadingCache.get(pattern).format(value); } catch (ExecutionException e) { e.printStackTrace(); } return value.toString(); } } ================================================ FILE: src/main/java/com/wuwenze/poi/util/POIUtil.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.util; import com.wuwenze.poi.config.Options; import com.wuwenze.poi.exception.ExcelKitRuntimeException; import java.io.File; import java.io.IOException; import java.io.OutputStream; import javax.servlet.http.HttpServletResponse; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.poi.ss.usermodel.DataValidation; import org.apache.poi.ss.usermodel.DataValidationConstraint; import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.xssf.streaming.SXSSFCell; import org.apache.poi.xssf.streaming.SXSSFRow; import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; /** * @author wuwenze */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class POIUtil { private static final int mDefaultRowAccessWindowSize = 100; private static SXSSFWorkbook newSXSSFWorkbook(int rowAccessWindowSize) { return new SXSSFWorkbook(rowAccessWindowSize); } public static SXSSFWorkbook newSXSSFWorkbook() { return POIUtil.newSXSSFWorkbook(POIUtil.mDefaultRowAccessWindowSize); } public static SXSSFSheet newSXSSFSheet(SXSSFWorkbook wb, String sheetName) { return wb.createSheet(sheetName); } public static SXSSFRow newSXSSFRow(SXSSFSheet sheet, int index) { return sheet.createRow(index); } public static SXSSFCell newSXSSFCell(SXSSFRow row, int index) { return row.createCell(index); } public static void setColumnWidth( SXSSFSheet sheet, int index, Short width, String value) { boolean widthNotHaveConfig = (null == width || width == -1); if (widthNotHaveConfig && !ValidatorUtil.isEmpty(value)) { sheet.setColumnWidth(index, (short) (value.length() * 2048)); } else { width = widthNotHaveConfig ? 200 : width; sheet.setColumnWidth(index, (short) (width * 35.7)); } } public static void setColumnCellRange(SXSSFSheet sheet, Options options, int firstRow, int endRow, int firstCell, int endCell) { if (null != options) { String[] datasource = options.get(); if (null != datasource && datasource.length > 0) { if (datasource.length > 100) { throw new ExcelKitRuntimeException("Options item too much."); } DataValidationHelper validationHelper = sheet.getDataValidationHelper(); DataValidationConstraint explicitListConstraint = validationHelper .createExplicitListConstraint(datasource); CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCell, endCell); DataValidation validation = validationHelper .createValidation(explicitListConstraint, regions); validation.setSuppressDropDownArrow(true); validation.createErrorBox("提示", "请从下拉列表选取"); validation.setShowErrorBox(true); sheet.addValidationData(validation); } } } public static void write(SXSSFWorkbook wb, OutputStream out) { try { if (null != out) { wb.write(out); out.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { if (null != out) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void download( SXSSFWorkbook wb, HttpServletResponse response, String filename) { try { OutputStream out = response.getOutputStream(); response.setContentType(Const.XLSX_CONTENT_TYPE); response.setHeader(Const.XLSX_HEADER_KEY, String.format(Const.XLSX_HEADER_VALUE_TEMPLATE, filename)); POIUtil.write(wb, out); } catch (IOException e) { e.printStackTrace(); } } public static Object convertByExp(Object propertyValue, String converterExp) throws Exception { try { String[] convertSource = converterExp.split(","); for (String item : convertSource) { String[] itemArray = item.split("="); if (itemArray[0].equals(propertyValue)) { return itemArray[1]; } } } catch (Exception e) { throw e; } return propertyValue; } public static int countNullCell(String ref, String ref2) { // excel2007最大行数是1048576,最大列数是16384,最后一列列名是XFD String xfd = ref.replaceAll("\\d+", ""); String xfd_1 = ref2.replaceAll("\\d+", ""); xfd = POIUtil.fillChar(xfd, 3, '@', true); xfd_1 = POIUtil.fillChar(xfd_1, 3, '@', true); char[] letter = xfd.toCharArray(); char[] letter_1 = xfd_1.toCharArray(); int res = (letter[0] - letter_1[0]) * 26 * 26 + (letter[1] - letter_1[1]) * 26 + (letter[2] - letter_1[2]); return res - 1; } private static String fillChar(String str, int len, char let, boolean isPre) { int len_1 = str.length(); if (len_1 < len) { if (isPre) { for (int i = 0; i < (len - len_1); i++) { str = let + str; } } else { for (int i = 0; i < (len - len_1); i++) { str = str + let; } } } return str; } public static void checkExcelFile(File file) { String filename = null != file ? file.getAbsolutePath() : null; if (null == filename || !file.exists()) { throw new ExcelKitRuntimeException("Excel file[" + filename + "] does not exist."); } if (!filename.endsWith(Const.XLSX_SUFFIX)) { throw new ExcelKitRuntimeException( "[" + filename + "]Only .xlsx formatted files are supported."); } } } ================================================ FILE: src/main/java/com/wuwenze/poi/util/PathUtil.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.util; import java.io.File; import lombok.AccessLevel; import lombok.NoArgsConstructor; /** * @author wuwenze */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class PathUtil { public static String getClasspath() { return PathUtil.class.getResource("/").getPath(); } public static String getFilePathByClasspath(String name) { return PathUtil.getClasspath() + name; } public static File getFileByClasspath(String name) { return new File(PathUtil.getFilePathByClasspath(name)); } } ================================================ FILE: src/main/java/com/wuwenze/poi/util/RegexUtil.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.util; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; import lombok.AccessLevel; import lombok.NoArgsConstructor; /** * @author wuwenze */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class RegexUtil { private final static LoadingCache mRegexPatternLoadingCache = CacheBuilder.newBuilder() .maximumSize(5) .build(new CacheLoader() { @Override public Pattern load(String pattern) { return Pattern.compile(pattern); } }); public static Boolean isMatches(String pattern, Object value) { try { String valueString = (String) value; return RegexUtil.mRegexPatternLoadingCache.get(pattern).matcher(valueString).matches(); } catch (ExecutionException e) { e.printStackTrace(); } return false; } } ================================================ FILE: src/main/java/com/wuwenze/poi/util/ValidatorUtil.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.util; import java.util.regex.Pattern; import lombok.AccessLevel; import lombok.NoArgsConstructor; /** * @author wuwenze */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ValidatorUtil { private static final Pattern PATTERN_NUMERIC = Pattern.compile("^(-?\\d+)(\\.\\d+)?$"); private static final Pattern PATTERN_EMAIL = Pattern .compile("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$"); private static final Pattern PATTERN_MOBILE_PHONE = Pattern.compile("^(1)\\d{10}$"); public static boolean match(String value, String regularExp) { return ValidatorUtil.isEmpty(value) ? false : Pattern.compile(regularExp).matcher(value).matches(); } /** * 判断是否为浮点数或者整数 * * @param value 字符串 * @return true Or false */ public static boolean isNumeric(String value) { return ValidatorUtil.isEmpty(value) ? false : ValidatorUtil.PATTERN_NUMERIC.matcher(value).matches(); } /** * 判断是否为正确的邮件格式 * * @param value 字符串 * @return true Or false */ public static boolean isEmail(String value) { return ValidatorUtil.isEmpty(value) ? false : ValidatorUtil.PATTERN_EMAIL.matcher(value).matches(); } /** * 判断字符串是否为合法手机号 * * @param value 字符串 * @return true Or false */ public static boolean isMobile(String value) { return ValidatorUtil.isEmpty(value) ? false : ValidatorUtil.PATTERN_MOBILE_PHONE.matcher(value).matches(); } /** * 判断是否为数字 * * @param value 字符串 * @return true Or false */ public static boolean isNumber(String value) { try { Integer.parseInt(value); } catch (Exception ex) { return false; } return true; } /** * 判断字符串是否为空 * * @param value 字符串 * @return true Or false */ public static boolean isEmpty(String value) { return null == value || "".equals(value.trim()) || "null".equalsIgnoreCase(value.trim()); } } ================================================ FILE: src/main/java/com/wuwenze/poi/validator/EmailValidator.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.validator; import com.wuwenze.poi.util.ValidatorUtil; /** * @author wuwenze */ public class EmailValidator implements Validator { @Override public String valid(Object value) { String valueString = (String) value; return ValidatorUtil.isEmail(valueString) ? null : "[" + valueString + "]不是正确的EMail."; } } ================================================ FILE: src/main/java/com/wuwenze/poi/validator/MobileValidator.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.validator; import com.wuwenze.poi.util.ValidatorUtil; /** * @author wuwenze */ public class MobileValidator implements Validator { @Override public String valid(Object value) { String valueString = (String) value; return ValidatorUtil.isMobile(valueString) ? null : "[" + valueString + "]不是正确的手机号码."; } } ================================================ FILE: src/main/java/com/wuwenze/poi/validator/Validator.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.validator; /** * @author wuwenze */ public interface Validator { /** * 验证单元格的值, 若验证失败, 请返回错误消息. * * @param value 当前单元格的值 * @return null or errorMessage */ String valid(Object value); } ================================================ FILE: src/main/java/com/wuwenze/poi/xlsx/ExcelXlsxReader.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.xlsx; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.wuwenze.poi.config.Options; import com.wuwenze.poi.convert.ReadConverter; import com.wuwenze.poi.exception.ExcelKitEncounterNoNeedXmlException; import com.wuwenze.poi.exception.ExcelKitReadConverterException; import com.wuwenze.poi.exception.ExcelKitRuntimeException; import com.wuwenze.poi.handler.ExcelReadHandler; import com.wuwenze.poi.pojo.ExcelErrorField; import com.wuwenze.poi.pojo.ExcelMapping; import com.wuwenze.poi.pojo.ExcelProperty; import com.wuwenze.poi.util.*; import com.wuwenze.poi.validator.Validator; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.ss.usermodel.BuiltinFormats; import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; /** * @author wuwenze */ public class ExcelXlsxReader extends DefaultHandler { private Integer mCurrentSheetIndex = -1, mCurrentRowIndex = 0, mCurrentCellIndex = 0; private ExcelCellType mNextCellType = ExcelCellType.STRING; private String mCurrentCellRef, mPreviousCellRef, mMaxCellRef; private SharedStringsTable mSharedStringsTable; private String mPreviousCellValue; private StylesTable mStylesTable; private Boolean mNextIsString = false; private Short mFormatIndex; private String mFormatString; private final ExcelMapping mExcelMapping; private final ExcelReadHandler mExcelReadHandler; private final Class mEntityClass; private final List mExcelRowObjectData = Lists.newArrayList(); private Integer mBeginReadRowIndex = Const.XLSX_DEFAULT_BEGIN_READ_ROW_INDEX; private final Object mEmptyCellValue = Const.XLSX_DEFAULT_EMPTY_CELL_VALUE; private final DataFormatter formatter = new DataFormatter(); public ExcelXlsxReader(Class entityClass,// ExcelMapping excelMapping, // ExcelReadHandler excelReadHandler) { this(entityClass, excelMapping, null, excelReadHandler); } public ExcelXlsxReader(Class entityClass,// ExcelMapping excelMapping, // Integer beginReadRowIndex,// ExcelReadHandler excelReadHandler) { mEntityClass = entityClass; mExcelMapping = excelMapping; if (null != beginReadRowIndex) { mBeginReadRowIndex = beginReadRowIndex; } mExcelReadHandler = excelReadHandler; } public void process(String fileName) throws ExcelKitRuntimeException { try { processAll(OPCPackage.open(fileName)); } catch (Exception e) { throw new ExcelKitRuntimeException("Only .xlsx formatted files are supported.", e); } } public void process(InputStream in) throws ExcelKitRuntimeException { try { processAll(OPCPackage.open(in)); } catch (Exception e) { throw new ExcelKitRuntimeException("Only .xlsx formatted files are supported.", e); } } private void processAll(OPCPackage pkg) throws IOException, OpenXML4JException, SAXException { XSSFReader xssfReader = new XSSFReader(pkg); mStylesTable = xssfReader.getStylesTable(); SharedStringsTable sst = xssfReader.getSharedStringsTable(); XMLReader parser = this.fetchSheetParser(sst); Iterator sheets = xssfReader.getSheetsData(); while (sheets.hasNext()) { mCurrentRowIndex = 0; mCurrentSheetIndex++; InputStream sheet = sheets.next(); InputSource sheetSource = new InputSource(sheet); parser.parse(sheetSource); sheet.close(); } pkg.close(); } public void process(String fileName, int sheetIndex) throws ExcelKitRuntimeException { try { this.processBySheet(sheetIndex, OPCPackage.open(fileName)); } catch (Exception e) { throw new ExcelKitRuntimeException("Only .xlsx formatted files are supported.", e); } } public void process(InputStream in, int sheetIndex) throws ExcelKitRuntimeException { try { this.processBySheet(sheetIndex, OPCPackage.open(in)); } catch (Exception e) { throw new ExcelKitRuntimeException("Only .xlsx formatted files are supported.", e); } } private void processBySheet(int sheetIndex, OPCPackage pkg) throws IOException, OpenXML4JException, SAXException { XSSFReader r = new XSSFReader(pkg); SharedStringsTable sst = r.getSharedStringsTable(); XMLReader parser = fetchSheetParser(sst); // 根据 rId# 或 rSheet# 查找sheet InputStream sheet = r.getSheet(Const.SAX_RID_PREFIX + (sheetIndex + 1)); mCurrentSheetIndex++; InputSource sheetSource = new InputSource(sheet); try { parser.parse(sheetSource); } catch (ExcelKitEncounterNoNeedXmlException e) { sheet = r.getSheet(Const.SAX_RID_PREFIX + (sheetIndex + 3)); sheetSource = new InputSource(sheet); parser.parse(sheetSource); } sheet.close(); pkg.close(); } @Override public void startElement( String uri, String localName, String name, Attributes attributes) { if ("sst".equals(name) || "styleSheet".equals(name)) { throw new ExcelKitEncounterNoNeedXmlException(); } // c => 单元格 if (Const.SAX_C_ELEMENT.equals(name)) { String ref = attributes.getValue(Const.SAX_R_ATTR); // 前一个单元格的位置 mPreviousCellRef = null == mPreviousCellRef ? ref : mCurrentCellRef; // 当前单元格的位置 mCurrentCellRef = ref; // Figure out if the value is an index in the SST String cellType = attributes.getValue(Const.SAX_T_ELEMENT); String cellStyleStr = attributes.getValue(Const.SAX_S_ATTR_VALUE); mNextIsString = (null != cellType && cellType.equals(Const.SAX_S_ATTR_VALUE)); // 设定单元格类型 this.setNextCellType(cellType, cellStyleStr); } mPreviousCellValue = ""; } @Override public void endElement(String uri, String localName, String name) { // Process the last contents as required. // Do now, as characters() may be called more than once if (mNextIsString) { int index = Integer.parseInt(mPreviousCellValue); mPreviousCellValue = new XSSFRichTextString(mSharedStringsTable.getEntryAt(index)) .toString(); mNextIsString = false; } // 处理单元格数据 if (Const.SAX_C_ELEMENT.equals(name)) { String value = this.getCellValue(mPreviousCellValue.trim()); // 空值补齐(中) if (!mCurrentCellRef.equals(mPreviousCellRef)) { for (int i = 0; i < POIUtil.countNullCell(mCurrentCellRef, mPreviousCellRef); i++) { mExcelRowObjectData.add(mCurrentCellIndex, mEmptyCellValue); mCurrentCellIndex++; } } mExcelRowObjectData.add(mCurrentCellIndex, value); mCurrentCellIndex++; } // 如果标签名称为 row ,这说明已到行尾,通知回调处理当前行的数据 else if (Const.SAX_ROW_ELEMENT.equals(name)) { if (mCurrentRowIndex == 0) { mMaxCellRef = mCurrentCellRef; } // 空值补齐(后) if (null != mMaxCellRef) { for (int i = 0; i <= POIUtil.countNullCell(mMaxCellRef, mCurrentCellRef); i++) { mExcelRowObjectData.add(mCurrentCellIndex, mEmptyCellValue); mCurrentCellIndex++; } } try { this.performVerificationAndProcessFlowRow(); } catch (Exception e) { e.printStackTrace(); } finally { mExcelRowObjectData.clear(); mCurrentRowIndex++; mCurrentCellIndex = 0; mPreviousCellRef = null; mCurrentCellRef = null; } } } @Override public void characters(char[] chars, int start, int length) { mPreviousCellValue = mPreviousCellValue.concat(new String(chars, start, length)); } private XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException { XMLReader parser = XMLReaderFactory.createXMLReader(Const.SAX_PARSER_CLASS); mSharedStringsTable = sst; parser.setContentHandler(this); return parser; } enum ExcelCellType { BOOL, ERROR, FORMULA, INLINESTR, STRING, NUMBER, DATE, NULL } private void setNextCellType(String cellType, String cellStyleStr) { mNextCellType = ExcelCellType.STRING; mFormatIndex = -1; mFormatString = null; if ("b".equals(cellType)) { mNextCellType = ExcelCellType.BOOL; } else if ("e".equals(cellType)) { mNextCellType = ExcelCellType.ERROR; } else if ("inlineStr".equals(cellType)) { mNextCellType = ExcelCellType.INLINESTR; } else if ("s".equals(cellType)) { mNextCellType = ExcelCellType.STRING; } else if ("str".equals(cellType)) { mNextCellType = ExcelCellType.FORMULA; } if (null != cellStyleStr) { int styleIndex = Integer.parseInt(cellStyleStr); XSSFCellStyle style = mStylesTable.getStyleAt(styleIndex); mFormatIndex = style.getDataFormat(); mFormatString = style.getDataFormatString(); if ("m/d/yy".equals(mFormatString)) { mNextCellType = mNextCellType.DATE; mFormatString = "yyyy-MM-dd hh:mm:ss.SSS"; } if (null == mFormatString) { mNextCellType = mNextCellType.NULL; mFormatString = BuiltinFormats.getBuiltinFormat(mFormatIndex); } } } private String getCellValue(String value) { String thisStr; switch (mNextCellType) { case BOOL: return value.charAt(0) == '0' ? "FALSE" : "TRUE"; case ERROR: return "\"ERROR:" + value + '"'; case FORMULA: return '"' + value + '"'; case INLINESTR: return new XSSFRichTextString(value).toString(); case STRING: return String.valueOf(value); case NUMBER: if (mFormatString != null) { thisStr = formatter.formatRawCellContents(Double.parseDouble(value), mFormatIndex, mFormatString).trim(); } else { thisStr = value; } thisStr = thisStr.replace("_", "").trim(); break; case DATE: thisStr = formatter.formatRawCellContents(Double.parseDouble(value), mFormatIndex, mFormatString); // 对日期字符串作特殊处理 thisStr = thisStr.replace(" ", "T"); break; default: thisStr = ""; break; } return thisStr; } private final static String CHECK_MAP_KEY_OF_VALUE = "CELL_VALUE"; private final static String CHECK_MAP_KEY_OF_ERROR = "CELL_ERROR"; private void performVerificationAndProcessFlowRow() throws Exception { if (mCurrentRowIndex >= mBeginReadRowIndex) { List propertyList = mExcelMapping.getPropertyList(); Integer excelRowDataSize = mExcelRowObjectData.size(); Integer excelMappingPropertySize = propertyList.size(); // 空值补齐(前) for (int i = 0; i < excelMappingPropertySize - excelRowDataSize; i++) { mExcelRowObjectData.add(i, mEmptyCellValue); } if (!this.rowObjectDataIsAllEmptyCellValue()) { Object entity = mEntityClass.newInstance(); List errorFields = Lists.newArrayList(); for (int i = 0; i < propertyList.size(); i++) { ExcelProperty property = propertyList.get(i); Map checkAndConvertPropertyRetMap = this.checkAndConvertProperty(i, property, mExcelRowObjectData.get(i)); Object errorFieldObject = checkAndConvertPropertyRetMap.get( ExcelXlsxReader.CHECK_MAP_KEY_OF_ERROR); if (null != errorFieldObject) { errorFields.add((ExcelErrorField) errorFieldObject); } if (errorFields.isEmpty()) { Object propertyValue = checkAndConvertPropertyRetMap.get( ExcelXlsxReader.CHECK_MAP_KEY_OF_VALUE); BeanUtil.setComplexProperty(entity, property.getName(), propertyValue); } } if (errorFields.isEmpty()) { mExcelReadHandler.onSuccess(mCurrentSheetIndex, mCurrentRowIndex, entity); return; } mExcelReadHandler.onError(mCurrentSheetIndex, mCurrentRowIndex, errorFields); } } } private boolean rowObjectDataIsAllEmptyCellValue() { int emptyObjectCount = 0; for (Object excelRowObjectData : mExcelRowObjectData) { if ((null == excelRowObjectData) // || excelRowObjectData.equals(mEmptyCellValue) // || ValidatorUtil.isEmpty((String) excelRowObjectData)) { emptyObjectCount++; } } return emptyObjectCount == mExcelRowObjectData.size(); } private Map checkAndConvertProperty(Integer cellIndex, ExcelProperty property, Object propertyValue) { if(null == propertyValue || ValidatorUtil.isEmpty((String) propertyValue) || Const.XLSX_DEFAULT_EMPTY_CELL_VALUE.equals(propertyValue)) { // required Boolean required = property.getRequired(); if (null != required && required) { return this.buildCheckAndConvertPropertyRetMap(cellIndex, property, propertyValue, "单元格的值必须填写"); } // empty cell doesn't need to check anymore return this.buildCheckAndConvertPropertyRetMap(cellIndex, property, null, null); } // maxLength Integer maxLength = property.getMaxLength(); if (-1 != maxLength) { if (String.valueOf(propertyValue).length() > maxLength) { return this.buildCheckAndConvertPropertyRetMap(cellIndex, property, propertyValue, "超过最大长度: " + maxLength); } } // dateFormat String dateFormat = property.getDateFormat(); if (!ValidatorUtil.isEmpty(dateFormat)) { try { // 时间格式转换后,直接返回。 Date parseDateValue = DateUtil.parse(dateFormat, propertyValue); return this.buildCheckAndConvertPropertyRetMap(cellIndex, property, parseDateValue, null); } catch (Exception e) { return this.buildCheckAndConvertPropertyRetMap(// cellIndex, property, propertyValue, "时间格式解析失败 [" + dateFormat + "]"); } } // options Options options = property.getOptions(); if (null != options) { Object[] values = options.get(); if (null != values && values.length > 0) { boolean containInOptions = false; for (Object value : values) { if (propertyValue.equals(value)) { containInOptions = true; break; } } if (!containInOptions) { return this.buildCheckAndConvertPropertyRetMap(// cellIndex, property, propertyValue, "[" + propertyValue + "]不是规定的下拉框的值"); } } } // regularExp String regularExp = property.getRegularExp(); if (!ValidatorUtil.isEmpty(regularExp)) { if (!RegexUtil.isMatches(regularExp, propertyValue)) { String regularExpMessage = property.getRegularExpMessage(); String validErrorMessage = !ValidatorUtil.isEmpty(regularExpMessage) ? regularExpMessage : "正则表达式校验失败 [" + regularExp + "]"; return this.buildCheckAndConvertPropertyRetMap(// cellIndex, property, propertyValue, validErrorMessage); } } // validator Validator validator = property.getValidator(); if (null != validator) { String validErrorMessage = validator.valid(propertyValue); if (null != validErrorMessage) { return this.buildCheckAndConvertPropertyRetMap(// cellIndex, property, propertyValue, validErrorMessage); } } // readConverterExp && readConverter (按照优先级处理) String readConverterExp = property.getReadConverterExp(); ReadConverter readConverter = property.getReadConverter(); if (!ValidatorUtil.isEmpty(readConverterExp)) { try { Object convertPropertyValue = POIUtil.convertByExp(propertyValue, readConverterExp); return this.buildCheckAndConvertPropertyRetMap(// cellIndex, property, convertPropertyValue, null); } catch (Exception e) { return this.buildCheckAndConvertPropertyRetMap(// cellIndex, property, propertyValue, "由于readConverterExp表达式的值不规范导致转换失败"); } } else if (null != readConverter) { try { return this.buildCheckAndConvertPropertyRetMap(// cellIndex, property, readConverter.convert(propertyValue), null); } catch (ExcelKitReadConverterException e) { return this.buildCheckAndConvertPropertyRetMap(// cellIndex, property, propertyValue, e.getMessage()); } } return this.buildCheckAndConvertPropertyRetMap(cellIndex, property, propertyValue, null); } private Map buildCheckAndConvertPropertyRetMap(Integer cellIndex, ExcelProperty property,// Object propertyValue, String validErrorMessage) { Map resultMap = Maps.newHashMap(); resultMap.put(ExcelXlsxReader.CHECK_MAP_KEY_OF_VALUE, propertyValue); if (null != validErrorMessage) { resultMap.put(ExcelXlsxReader.CHECK_MAP_KEY_OF_ERROR, ExcelErrorField.builder()// .cellIndex(cellIndex)// .column(property.getColumn())// .name(property.getName())// .errorMessage(validErrorMessage)// .build()); } return resultMap; } } ================================================ FILE: src/main/java/com/wuwenze/poi/xlsx/ExcelXlsxWriter.java ================================================ /* * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com). * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wuwenze.poi.xlsx; import com.wuwenze.poi.convert.WriteConverter; import com.wuwenze.poi.exception.ExcelKitRuntimeException; import com.wuwenze.poi.pojo.ExcelMapping; import com.wuwenze.poi.pojo.ExcelProperty; import com.wuwenze.poi.util.DateUtil; import com.wuwenze.poi.util.POIUtil; import com.wuwenze.poi.util.ValidatorUtil; import java.text.ParseException; import java.util.Date; import java.util.List; import org.apache.commons.beanutils.BeanUtils; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Comment; import org.apache.poi.ss.usermodel.DataFormat; import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.xssf.streaming.SXSSFCell; import org.apache.poi.xssf.streaming.SXSSFDrawing; import org.apache.poi.xssf.streaming.SXSSFRow; import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFRichTextString; /** * @author wuwenze */ public class ExcelXlsxWriter { private final ExcelMapping mExcelMapping; private final Integer mMaxSheetRecords; public ExcelXlsxWriter(ExcelMapping excelMapping, Integer maxSheetRecords) { mExcelMapping = excelMapping; mMaxSheetRecords = maxSheetRecords; } /** * 构建xlsxWorkbook对象 * * @param data 数据集 * @param isTemplate 是否是导出模板 * @return SXSSFWorkbook */ public SXSSFWorkbook generateXlsxWorkbook(List data, boolean isTemplate) { SXSSFWorkbook workbook = POIUtil.newSXSSFWorkbook(); List propertyList = mExcelMapping.getPropertyList(); double sheetNo = Math.ceil(data.size() / (double) mMaxSheetRecords); for (int index = 0; index <= (sheetNo == 0.0 ? sheetNo : sheetNo - 1); index++) { String sheetName = mExcelMapping.getName() + (index == 0 ? "" : "_" + index); SXSSFSheet sheet = generateXlsxHeader(workbook, propertyList, sheetName, isTemplate); if (null != data && data.size() > 0) { int startNo = index * mMaxSheetRecords; int endNo = Math.min(startNo + mMaxSheetRecords, data.size()); for (int i = startNo; i < endNo; i++) { SXSSFRow bodyRow = POIUtil.newSXSSFRow(sheet, i + 1 - startNo); for (int j = 0; j < propertyList.size(); j++) { SXSSFCell cell = POIUtil.newSXSSFCell(bodyRow, j); ExcelXlsxWriter.buildCellValueByExcelProperty(cell, data.get(i), propertyList.get(j)); } } } } return workbook; } private SXSSFSheet generateXlsxHeader(SXSSFWorkbook workbook, List propertyList, String sheetName, boolean isTemplate) { SXSSFDrawing sxssfDrawing = null; SXSSFSheet sheet = POIUtil.newSXSSFSheet(workbook, sheetName); SXSSFRow headerRow = POIUtil.newSXSSFRow(sheet, 0); for (int i = 0; i < propertyList.size(); i++) { ExcelProperty property = propertyList.get(i); SXSSFCell cell = POIUtil.newSXSSFCell(headerRow, i); POIUtil.setColumnWidth(sheet, i, property.getWidth(), property.getColumn()); if (isTemplate) { // cell range POIUtil.setColumnCellRange(sheet, property.getOptions(), 1, mMaxSheetRecords, i, i); // cell comment. if (null == sxssfDrawing) { sxssfDrawing = sheet.createDrawingPatriarch(); } if (!ValidatorUtil.isEmpty(property.getComment())) { // int col1, int row1, int col2, int row2 Comment cellComment = sxssfDrawing.createCellComment(// new XSSFClientAnchor(0, 0, 0, 0, i, 0,i+2, i+2)); XSSFRichTextString xssfRichTextString = new XSSFRichTextString( property.getComment()); Font commentFormatter = workbook.createFont(); xssfRichTextString.applyFont(commentFormatter); cellComment.setString(xssfRichTextString); cell.setCellComment(cellComment); } } cell.setCellStyle(getHeaderCellStyle(workbook)); String headerColumnValue = property.getColumn(); if (isTemplate && null != property.getRequired() && property.getRequired()) { headerColumnValue = (headerColumnValue + "[*]"); } cell.setCellValue(headerColumnValue); } return sheet; } private static void buildCellValueByExcelProperty(SXSSFCell cell, Object entity, ExcelProperty property) { Object cellValue; try { cellValue = BeanUtils.getProperty(entity, property.getName()); } catch (Throwable e) { throw new ExcelKitRuntimeException(e); } if (null != cellValue) { String dateFormat = property.getDateFormat(); if (!ValidatorUtil.isEmpty(dateFormat)) { if (cellValue instanceof Date) { cell.setCellValue(DateUtil.format(dateFormat, (Date) cellValue)); } else if (cellValue instanceof String) { try { Date parse = DateUtil.ENGLISH_LOCAL_DF.parse((String) cellValue); cell.setCellValue(DateUtil.format(dateFormat, parse)); } catch (ParseException e) { e.printStackTrace(); } return; } } // writeConverterExp && writeConverter String writeConverterExp = property.getWriteConverterExp(); WriteConverter writeConverter = property.getWriteConverter(); if (!ValidatorUtil.isEmpty(writeConverterExp)) { try { cellValue = POIUtil.convertByExp(cellValue, writeConverterExp); } catch (Throwable e) { throw new ExcelKitRuntimeException(e); } } else if (null != writeConverter) { cell.setCellValue(writeConverter.convert(cellValue)); return; } cell.setCellValue(String.valueOf(cellValue)); } } private CellStyle mHeaderCellStyle = null; public CellStyle getHeaderCellStyle(SXSSFWorkbook wb) { if (null == mHeaderCellStyle) { mHeaderCellStyle = wb.createCellStyle(); Font font = wb.createFont(); mHeaderCellStyle.setFillForegroundColor((short) 12); mHeaderCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); mHeaderCellStyle.setBorderTop(BorderStyle.DOTTED); mHeaderCellStyle.setBorderRight(BorderStyle.DOTTED); mHeaderCellStyle.setBorderBottom(BorderStyle.DOTTED); mHeaderCellStyle.setBorderLeft(BorderStyle.DOTTED); mHeaderCellStyle.setAlignment(HorizontalAlignment.LEFT);// 对齐 mHeaderCellStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.GREEN.getIndex()); mHeaderCellStyle.setFillBackgroundColor(HSSFColor.HSSFColorPredefined.GREEN.getIndex()); font.setColor(HSSFColor.HSSFColorPredefined.WHITE.getIndex()); // 应用标题字体到标题样式 mHeaderCellStyle.setFont(font); //设置单元格文本形式 DataFormat dataFormat = wb.createDataFormat(); mHeaderCellStyle.setDataFormat(dataFormat.getFormat("@")); } return mHeaderCellStyle; } }