Repository: zhangdaiscott/autopoi Branch: master Commit: bcac4e081df9 Files: 161 Total size: 641.4 KB Directory structure: gitextract_0g1d_hnq/ ├── .gitattributes ├── .gitignore ├── LICENSE ├── POI_5升级说明.md ├── README.md ├── README.zh-CN.md ├── autopoi/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── jeecgframework/ │ │ ├── core/ │ │ │ └── util/ │ │ │ └── ApplicationContextUtil.java │ │ ├── dict/ │ │ │ └── service/ │ │ │ └── AutoPoiDictServiceI.java │ │ └── poi/ │ │ ├── cache/ │ │ │ ├── ExcelCache.java │ │ │ ├── ImageCache.java │ │ │ ├── WordCache.java │ │ │ ├── manager/ │ │ │ │ ├── FileLoade.java │ │ │ │ └── POICacheManager.java │ │ │ └── package-info.java │ │ ├── consts/ │ │ │ └── ImageScaleMode.java │ │ ├── entity/ │ │ │ └── ImageEntity.java │ │ ├── excel/ │ │ │ ├── ExcelExportUtil.java │ │ │ ├── ExcelImportCheckUtil.java │ │ │ ├── ExcelImportUtil.java │ │ │ ├── ExcelToHtmlUtil.java │ │ │ ├── annotation/ │ │ │ │ ├── Excel.java │ │ │ │ ├── ExcelCollection.java │ │ │ │ ├── ExcelEntity.java │ │ │ │ ├── ExcelIgnore.java │ │ │ │ ├── ExcelTarget.java │ │ │ │ └── ExcelVerify.java │ │ │ ├── entity/ │ │ │ │ ├── ExcelBaseParams.java │ │ │ │ ├── ExportParams.java │ │ │ │ ├── ImportParams.java │ │ │ │ ├── TemplateExportParams.java │ │ │ │ ├── enmus/ │ │ │ │ │ ├── CellValueType.java │ │ │ │ │ ├── ExcelStyleType.java │ │ │ │ │ └── ExcelType.java │ │ │ │ ├── params/ │ │ │ │ │ ├── ExcelBaseEntity.java │ │ │ │ │ ├── ExcelCollectionParams.java │ │ │ │ │ ├── ExcelExportEntity.java │ │ │ │ │ ├── ExcelForEachParams.java │ │ │ │ │ ├── ExcelImportEntity.java │ │ │ │ │ ├── ExcelTemplateParams.java │ │ │ │ │ ├── ExcelVerifyEntity.java │ │ │ │ │ └── MergeEntity.java │ │ │ │ ├── result/ │ │ │ │ │ ├── ExcelImportResult.java │ │ │ │ │ └── ExcelVerifyHanlderResult.java │ │ │ │ ├── sax/ │ │ │ │ │ └── SaxReadCellEntity.java │ │ │ │ └── vo/ │ │ │ │ └── PoiBaseConstants.java │ │ │ ├── export/ │ │ │ │ ├── ExcelBatchExportServer.java │ │ │ │ ├── ExcelExportServer.java │ │ │ │ ├── base/ │ │ │ │ │ ├── ExcelExportBase.java │ │ │ │ │ └── ExportBase.java │ │ │ │ ├── styler/ │ │ │ │ │ ├── AbstractExcelExportStyler.java │ │ │ │ │ ├── ExcelExportStylerBorderImpl.java │ │ │ │ │ ├── ExcelExportStylerColorImpl.java │ │ │ │ │ ├── ExcelExportStylerDefaultImpl.java │ │ │ │ │ └── IExcelExportStyler.java │ │ │ │ └── template/ │ │ │ │ └── ExcelExportOfTemplateUtil.java │ │ │ ├── graph/ │ │ │ │ ├── builder/ │ │ │ │ │ └── ExcelChartBuildService.java │ │ │ │ ├── constant/ │ │ │ │ │ ├── ExcelGraphElementType.java │ │ │ │ │ └── ExcelGraphType.java │ │ │ │ ├── entity/ │ │ │ │ │ ├── ExcelGraph.java │ │ │ │ │ ├── ExcelGraphDefined.java │ │ │ │ │ ├── ExcelGraphElement.java │ │ │ │ │ └── ExcelTitleCell.java │ │ │ │ └── package-info.java │ │ │ ├── html/ │ │ │ │ ├── ExcelToHtmlServer.java │ │ │ │ └── helper/ │ │ │ │ ├── CellValueHelper.java │ │ │ │ ├── MergedRegionHelper.java │ │ │ │ ├── StylerHelper.java │ │ │ │ └── excelStyle.css │ │ │ └── imports/ │ │ │ ├── CellValueServer.java │ │ │ ├── ExcelImportServer.java │ │ │ ├── base/ │ │ │ │ ├── ImportBaseService.java │ │ │ │ └── ImportFileServiceI.java │ │ │ ├── package-info.java │ │ │ ├── sax/ │ │ │ │ ├── SaxReadExcel.java │ │ │ │ ├── SheetHandler.java │ │ │ │ └── parse/ │ │ │ │ ├── ISaxRowRead.java │ │ │ │ └── SaxRowRead.java │ │ │ └── verifys/ │ │ │ ├── BaseVerifyHandler.java │ │ │ └── VerifyHandlerServer.java │ │ ├── exception/ │ │ │ ├── excel/ │ │ │ │ ├── ExcelExportException.java │ │ │ │ ├── ExcelImportException.java │ │ │ │ └── enums/ │ │ │ │ ├── ExcelExportEnum.java │ │ │ │ └── ExcelImportEnum.java │ │ │ └── word/ │ │ │ ├── WordExportException.java │ │ │ └── enmus/ │ │ │ └── WordExportEnum.java │ │ ├── handler/ │ │ │ ├── impl/ │ │ │ │ ├── ExcelDataHandlerDefaultImpl.java │ │ │ │ └── package-info.java │ │ │ ├── inter/ │ │ │ │ ├── IExcelDataHandler.java │ │ │ │ ├── IExcelDictHandler.java │ │ │ │ ├── IExcelExportServer.java │ │ │ │ ├── IExcelExportServerEnhanced.java │ │ │ │ ├── IExcelReadRowHanlder.java │ │ │ │ ├── IExcelVerifyHandler.java │ │ │ │ └── IWriter.java │ │ │ └── package-info.java │ │ ├── package-info.java │ │ ├── util/ │ │ │ ├── ExcelUtil.java │ │ │ ├── JsonParser.java │ │ │ ├── MyX509TrustManager.java │ │ │ ├── PoiCellUtil.java │ │ │ ├── PoiElUtil.java │ │ │ ├── PoiExcelGraphDataUtil.java │ │ │ ├── PoiExcelTempUtil.java │ │ │ ├── PoiFunctionUtil.java │ │ │ ├── PoiMergeCellUtil.java │ │ │ ├── PoiPublicUtil.java │ │ │ ├── PoiReflectorUtil.java │ │ │ └── PoiSheetUtility.java │ │ └── word/ │ │ ├── WordExportUtil.java │ │ ├── entity/ │ │ │ ├── MyXWPFDocument.java │ │ │ ├── WordImageEntity.java │ │ │ └── params/ │ │ │ ├── ExcelListEntity.java │ │ │ └── ListParamEntity.java │ │ └── parse/ │ │ ├── ParseWord07.java │ │ └── excel/ │ │ ├── ExcelEntityParse.java │ │ └── ExcelMapParse.java │ └── test/ │ ├── java/ │ │ ├── DaoChuBigDataTest.java │ │ ├── DaoChuSheetTest.java │ │ ├── DaoChuTest.java │ │ ├── DaoChuWordTest.java │ │ ├── ExcelToHtmlTest.java │ │ ├── ImportExcelTest.java │ │ ├── Poi541ApiCompatibilityTest.java │ │ ├── Poi541ComprehensiveTest.java │ │ ├── TestImageScale.java │ │ └── vo/ │ │ ├── TestDateEntity.java │ │ └── TestEntity.java │ └── resources/ │ └── templates/ │ ├── ExcelImportDateTest.xlsx │ ├── test.xlsx │ ├── testImageScale.xlsx │ ├── testNextMarge.xlsx │ ├── 专项支出用款申请书.xls │ └── 纳税信息.docx ├── autopoi-spring-boot-2-starter/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── jeecgframework/ │ └── poi/ │ └── excel/ │ ├── def/ │ │ ├── BasePOIConstants.java │ │ ├── MapExcelConstants.java │ │ ├── NormalExcelConstants.java │ │ ├── TemplateExcelConstants.java │ │ └── TemplateWordConstants.java │ └── view/ │ ├── JeecgEntityExcelView.java │ ├── JeecgMapExcelView.java │ ├── JeecgTemplateExcelView.java │ ├── JeecgTemplateWordView.java │ └── MiniAbstractExcelView.java ├── autopoi-spring-boot-3-starter/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── jeecgframework/ │ └── poi/ │ └── excel/ │ ├── def/ │ │ ├── BasePOIConstants.java │ │ ├── MapExcelConstants.java │ │ ├── NormalExcelConstants.java │ │ ├── TemplateExcelConstants.java │ │ └── TemplateWordConstants.java │ └── view/ │ ├── JeecgEntityExcelView.java │ ├── JeecgMapExcelView.java │ ├── JeecgTemplateExcelView.java │ ├── JeecgTemplateWordView.java │ └── MiniAbstractExcelView.java ├── deploy.bat ├── docs/ │ └── 修改日志.log ├── install.bat ├── pom.xml └── 打包构建.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.js linguist-language=Java *.css linguist-language=Java *.html linguist-language=Java *.vue linguist-language=Java *.sql linguist-language=Java ================================================ FILE: .gitignore ================================================ ## ide **/.idea *.iml ## backend **/target **/logs ## front **/*.lock ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2019 scott 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. ================================================ FILE: POI_5升级说明.md ================================================ # AutoPOI v2.0.0 升级指南 ## 版本信息 - **当前版本**: 2.0.0 - **发布日期**: 2025-10-22 - **POI 版本**: 5.4.1(从 4.1.2 升级) ## 🎯 重大更新 ### 1. POI 升级到 5.4.1 - ✅ 性能提升 20-30% - ✅ 大数据导出优化(10万行 < 1秒) - ✅ 更好的内存管理 - ✅ 修复多个已知问题 ### 2. Spring Boot 多版本支持 - ✅ Spring Boot 2.x(`autopoi-spring-boot-2-starter`) - ✅ Spring Boot 3.x(`autopoi-spring-boot-3-starter`) - ✅ Jakarta EE 完整适配 ### 3. 依赖更新 - Apache POI: 4.1.2 → 5.4.1 - Commons IO: 2.11.0 → 2.20.0 - Log4j: 升级到 2.24.3 ## 📦 Maven 依赖更新 ### Spring Boot 2.x 项目 ```xml org.jeecgframework autopoi-web 1.4.18 org.jeecgframework autopoi-spring-boot-2-starter 2.0.0 ``` ### Spring Boot 3.x 项目 ```xml org.jeecgframework autopoi-spring-boot-3-starter 2.0.0 ``` **注意**: Spring Boot 3.x 需要修改 Servlet 导入: ```java // 修改前 import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // 修改后 import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; ``` ### 纯 Java 项目 ```xml org.jeecgframework autopoi 2.0.0 ``` ## ⚠️ 重要变更 ### 1. 模块重命名 - `autopoi-web` → `autopoi-spring-boot-2-starter` ### 2. 依赖变更 #### ❌ 已删除的依赖 **`poi-ooxml-schemas` 依赖已移除** ```xml org.apache.poi poi-ooxml-schemas 1.4 ``` **删除原因:** 1. **POI 5.x 架构改进** - 从 POI 5.0 开始,`poi-ooxml` 依赖已经内置了必要的 schema 定义 2. **依赖简化** - POI 5.x 使用了更轻量的 `poi-ooxml-lite` 替代方案,减少了包体积 3. **避免冲突** - 旧版本的 `poi-ooxml-schemas` 包体积达 15MB+,容易与其他依赖冲突 4. **自动传递** - `poi-ooxml` 5.x 会自动引入所需的 schema 类,无需手动添加 **迁移操作:** - ✅ 直接删除 `poi-ooxml-schemas` 依赖即可 - ✅ 只保留 `poi` 和 `poi-ooxml` 依赖 - ✅ 无需任何代码修改 ```xml org.apache.poi poi 5.4.1 org.apache.poi poi-ooxml 5.4.1 ``` **验证方法:** ```bash # 检查依赖树,确认没有 poi-ooxml-schemas mvn dependency:tree | grep poi ``` ### 3. 不兼容的 API(已废弃) - ❌ **图表生成功能暂不可用**(`ExcelChartBuildService`) - 原因:POI 5.x 的图表 API 完全重构 - 影响:如果使用了图表生成功能,需要暂时移除或使用其他方案 ### 4. API 变更(内部已适配,无需修改代码) 以下变更已在内部处理,用户代码无需修改: - `cell.getCellTypeEnum()` → `cell.getCellType()` - `HSSFDateUtil` → `DateUtil` - `SharedStringsTable` → `SharedStrings` ## 📝 迁移步骤 ### 步骤 1: 更新依赖 根据你的项目类型,更新 `pom.xml` 中的依赖(参考上面的配置)。 ### 步骤 2: 修改导入(仅 Spring Boot 3.x) 如果升级到 Spring Boot 3.x,将 `javax.servlet` 改为 `jakarta.servlet`。 ### 步骤 3: 检查图表功能 如果使用了 `ExcelChartBuildService`,需要暂时移除或寻找替代方案。 ### 步骤 4: 测试验证 ```bash mvn clean compile mvn test ``` ### 步骤 5: 修复 Workbook 关闭问题 POI 5.x 要求显式关闭 Workbook: ```java // 推荐写法 try (FileOutputStream fos = new FileOutputStream("output.xlsx")) { workbook.write(fos); } finally { if (workbook != null) { workbook.close(); // 必须显式关闭 } } ``` ## ✅ 功能支持对照表 | 功能 | v1.4.x | v2.0.0 | 说明 | |------|--------|--------|------| | Excel 导入导出 | ✅ | ✅ | 性能提升 | | 注解驱动 | ✅ | ✅ | 完全兼容 | | 多表头 | ✅ | ✅ | 完全兼容 | | 多 Sheet | ✅ | ✅ | 完全兼容 | | 模板导出 | ✅ | ✅ | 完全兼容 | | 大数据导出 | ✅ | ✅ | 性能优化 | | Word 导出 | ✅ | ✅ | 完全兼容 | | 图表生成 | ⚠️ | ❌ | 暂不支持 | | Spring Boot 2.x | ✅ | ✅ | 完全兼容 | | Spring Boot 3.x | ❌ | ✅ | 新增支持 | ## 🐛 已修复问题 1. ✅ 大数据导出性能问题 2. ✅ Workbook 资源泄漏问题 3. ✅ 依赖冲突问题(commons-io、log4j) 4. ✅ 生成的 Excel 文件无法打开问题 ## 📚 相关资源 - [完整使用文档](./README.md) - [GitHub Issues](https://github.com/zhangdaiscott/autopoi/issues) - [示例代码](./autopoi/src/test/java/) ## 💡 常见问题 ### Q1: 升级后编译失败? 检查是否正确更新了依赖名称:`autopoi-web` → `autopoi-spring-boot-2-starter` ### Q2: Spring Boot 3 项目报错? 确保已将 `javax.servlet` 改为 `jakarta.servlet` ### Q3: 生成的 Excel 无法打开? 确保在写入后显式关闭 Workbook 对象(参考步骤5) ### Q4: 图表功能不可用? POI 5.x 的图表 API 已重构,该功能暂未适配,建议使用其他图表库 ### Q5: 性能有提升吗? 是的!测试显示性能提升 20-30%,大数据导出速度显著优化 ## 🎉 升级建议 - ✅ **推荐升级**: 如果不使用图表功能 - ⚠️ **谨慎升级**: 如果依赖图表生成功能 - ✅ **强烈推荐**: Spring Boot 3.x 新项目 ================================================ FILE: README.md ================================================ [中文](./README.zh-CN.md) | English AutoPOI (Excel and Word Easy Utility) =========================== AutoPOI, as its name suggests "auto", pursues automation. It enables anyone without POI experience to quickly implement Excel import/export and Word template export in a foolproof manner. You can complete Excel import/export with just 5 lines of code. Current Version: 2.0.4 (Released: 2025-12-21) --------------------------- Key Features of AutoPOI -------------------------- 1. Elegant design, easy to use 2. Rich interfaces, easy to extend 3. Many default values, write less do more 4. AbstractView support, web export made simple --------------------------- Main Utility Classes --------------------------- 1. ExcelExportUtil - Excel export (normal export, template export) 2. ExcelImportUtil - Excel import 3. WordExportUtil - Word export (only supports docx, doc version has image bugs in POI, not supported yet) --------------------------- Difference Between XLS and XLSX Export --------------------------- 1. Export time: XLS is 2-3x faster than XLSX 2. Export size: XLS is 2-3x larger than XLSX or more 3. Need to consider both network speed and local processing speed --------------------------- Project Modules --------------------------- 1. autopoi-parent - Parent POM 2. autopoi - Core utility package for Excel export/import and Word export 3. autopoi-spring-boot-2-starter - Spring Boot 2.x support (compatible with javax.servlet) 4. autopoi-spring-boot-3-starter - Spring Boot 3.x support (compatible with jakarta.servlet) 5. SAX import uses xercesImpl package (may cause unexpected issues), Word export uses poi-scratchpad, all as optional dependencies -------------------------- Maven Dependencies -------------------------- **For Spring Boot 2.x projects:** ```xml org.jeecgframework autopoi-spring-boot-2-starter 2.0.4 ``` **For Spring Boot 3.x projects:** ```xml org.jeecgframework autopoi-spring-boot-3-starter 2.0.4 ``` **For pure Java projects (without Spring):** ```xml org.jeecgframework autopoi 2.0.4 ``` -------------------------- Template Expression Support -------------------------- - Space separation - Ternary operator: {{test ? obj:obj2}} - n: Indicates numeric cell type {{n:}} - le: Represents length {{le:()}} used in if/else {{le:() > 8 ? obj1 : obj2}} - fd: Format date {{fd:(obj;yyyy-MM-dd)}} - fn: Format number {{fn:(obj;###.00)}} - fe: Iterate data, create row - !fe: Iterate data without creating row - $fe: Insert by moving down, move current and below rows down by .size() rows, then insert - !if: Delete current column {{!if:(test)}} - Single quotes for constant values '', e.g., '1' outputs 1 --------------------------- Export Examples --------------------------- 1. Annotations - Both import and export are annotation-based. Add annotations to entities to mark export objects and perform operations. ```Java @ExcelTarget("courseEntity") public class CourseEntity implements java.io.Serializable { /** Primary Key */ private String id; /** Course Name */ @Excel(name = "Course Name", orderNum = "1", needMerge = true) private String name; /** Teacher */ @ExcelEntity(id = "yuwen") @ExcelVerify() private TeacherEntity teacher; /** Math Teacher */ @ExcelEntity(id = "shuxue") private TeacherEntity shuxueteacher; @ExcelCollection(name = "Students", orderNum = "4") private List students; } ``` 2. Basic Export Pass export parameters, export object, and object list to complete the export. ```Java HSSFWorkbook workbook = ExcelExportUtil.exportExcel(new ExportParams( "2412312", "Test", "Test"), CourseEntity.class, list); ``` 3. Export with Index Set a parameter value to add an index column in the export. ```Java ExportParams params = new ExportParams("2412312", "Test", "Test"); params.setAddIndex(true); HSSFWorkbook workbook = ExcelExportUtil.exportExcel(params, TeacherEntity.class, telist); ``` 4. Export Map Create annotation-like collections to complete Map export. ```Java List entity = new ArrayList(); entity.add(new ExcelExportEntity("Name", "name")); entity.add(new ExcelExportEntity("Gender", "sex")); List> list = new ArrayList>(); Map map; for (int i = 0; i < 10; i++) { map = new HashMap(); map.put("name", "1" + i); map.put("sex", "2" + i); list.add(map); } HSSFWorkbook workbook = ExcelExportUtil.exportExcel(new ExportParams( "Test", "Test"), entity, list); ``` 5. Template Export Complete export based on template configuration. ```Java TemplateExportParams params = new TemplateExportParams(); params.setHeadingRows(2); params.setHeadingStartRow(2); Map map = new HashMap(); map.put("year", "2013"); map.put("sunCourses", list.size()); Map obj = new HashMap(); map.put("obj", obj); obj.put("name", list.size()); params.setTemplateUrl("org/jeecgframework/poi/excel/doc/exportTemp.xls"); Workbook book = ExcelExportUtil.exportExcel(params, CourseEntity.class, list, map); ``` 6. Import Set import parameters, pass file or stream to get the corresponding list. ```Java ImportParams params = new ImportParams(); params.setTitleRows(2); params.setHeadRows(2); //params.setSheetNum(9); params.setNeedSave(true); long start = new Date().getTime(); List list = ExcelImportUtil.importExcel(new File( "d:/tt.xls"), CourseEntity.class, params); ``` 7. Seamless Spring MVC Integration Excel export done with just a few lines. ```Java @RequestMapping(value = "/exportXls") public ModelAndView exportXls(HttpServletRequest request, HttpServletResponse response) { ModelAndView mv = new ModelAndView(new JeecgEntityExcelView()); List pageList = jeecgDemoService.list(); // Export file name mv.addObject(NormalExcelConstants.FILE_NAME, "Export Excel File Name"); // Annotated object Class mv.addObject(NormalExcelConstants.CLASS, JeecgDemo.class); // Custom table parameters mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("Custom Export Excel Title", "Custom Sheet Name")); // Export data list mv.addObject(NormalExcelConstants.DATA_LIST, pageList); return mv; } ``` | Custom View | Purpose | Description | | ------ | ------ | ------ | | JeecgEntityExcelView | Entity object export view | e.g., List<JeecgDemo> | | JeecgMapExcelView | Map object export view | List<Map<String, String>> list | | JeecgTemplateExcelView | Excel template export view | - | | JeecgTemplateWordView | Word template export view | - | 8. Excel Import Validation Filter data that doesn't meet rules, append error messages to Excel. Provides common validation rules and generic validation interface. ```Java /** * Email validation */ @Excel(name = "Email", width = 25) @ExcelVerify(isEmail = true, notNull = true) private String email; /** * Mobile phone validation */ @Excel(name = "Mobile", width = 20) @ExcelVerify(isMobile = true, notNull = true) private String mobile; ExcelImportResult result = ExcelImportUtil.importExcelVerify( new File("d:/tt.xls"), ExcelVerifyEntity.class, params); for (int i = 0; i < result.getList().size(); i++) { System.out.println(ReflectionToStringBuilder.toString(result.getList().get(i))); } ``` 9. Import Map Set import parameters, pass file or stream to get the corresponding list. Custom Key requires implementing IExcelDataHandler interface. ```Java ImportParams params = new ImportParams(); List> list = ExcelImportUtil.importExcel(new File( "d:/tt.xls"), Map.class, params); ``` 10. Dictionary Usage Add dicCode="" in the entity property Excel annotation, where dicCode is the Code of the data dictionary in the jeecg system. ```Java @Excel(name="Gender", width=15, dicCode="sex") private java.lang.String sex; ``` 11. Dictionary Table Usage dictTable is the database table name, dicCode is the associated field name, dicText is the field corresponding to the content displayed in Excel. ```Java @Excel(name="Department", dictTable="t_s_depart", dicCode="id", dicText="departname") private java.lang.String depart; ``` 12. Replace Usage If database stores 0/1, Excel cells display Female/Male. ```Java @Excel(name="Test Replace", width=15, replace={"Male_1","Female_0"}) private java.lang.String fdReplace; ``` 13. Advanced Field Conversion - exportConvert: Set to true to replace values during export, add a method with "convert" prefix before the original get method name. - importConvert: Set to true to replace values during import, add a method with "convert" prefix before the original set method name. ```Java @Excel(name="Test Convert", width=15, exportConvert=true, importConvert=true) private java.lang.String fdConvert; /** * Conversion example: Add suffix to the field value * @return */ public String convertgetFdConvert(){ return this.fdConvert + " Yuan"; } /** * Conversion example: Replace "Yuan" in Excel cell * @return */ public void convertsetFdConvert(String fdConvert){ this.fdConvert = fdConvert.replace(" Yuan", ""); } ``` --------------------------- Excel Annotation Reference --------------------------- **@Excel** | Property | Type | Default | Description | |----------------|----------|------------------|------------------------------------------------------------------------| | name | String | null | Column name, supports name_id | | needMerge | boolean | false | Whether to merge cells vertically (for single cells in a list, merge multiple rows created by list) | | orderNum | String | "0" | Column order, supports name_id | | replace | String[] | {} | Value replacement, export {a_id,b_id}, import reversed | | savePath | String | "upload" | Import file save path, can be filled for images, default is upload/className/ | | type | int | 1 | Export type: 1=text, 2=image, 3=function, 10=number, default is text | | width | double | 10 | Column width | | height | double | 10 | Column height (will be deprecated, use @ExcelTarget height instead) | | isStatistics | boolean | false | Auto statistics, append a statistics row with all data summed | | isHyperlink | boolean | FALSE | Hyperlink, need to implement interface to return object | | isImportField | boolean | TRUE | Validate field exists in imported Excel, supports name_id | | exportFormat | String | "" | Export date format | | importFormat | String | "" | Import date format | | format | String | "" | Date format, equivalent to setting both exportFormat and importFormat | | databaseFormat | String | "yyyyMMddHHmmss" | Database format for string type date fields | | numFormat | String | "" | Number format, Pattern parameter, uses DecimalFormat | | imageType | int | 1 | Image type: 1=from file, 2=from database, default is file | | suffix | String | "" | Text suffix, e.g., % makes 90 become 90% | | isWrap | boolean | TRUE | Whether to wrap, supports \n | | mergeRely | int[] | {} | Merge cell dependencies, e.g., {0} for second column based on first | | mergeVertical | boolean | false | Vertically merge cells with same content | | fixedIndex | int | -1 | Corresponds to Excel column, ignore name | | isColumnHidden | boolean | FALSE | Export hidden column | **@ExcelCollection** | Property | Type | Default | Description | |----------|----------|-----------------|------------------| | id | String | null | Define ID | | name | String | null | Define collection column name, supports name_id | | orderNum | int | 0 | Order, supports name_id | | type | Class | ArrayList.class | Used to create objects during import | **Single Table Export Entity Example** ```Java public class SysUser implements Serializable { /** id */ private String id; /** Login Account */ @Excel(name = "Login Account", width = 15) private String username; /** Real Name */ @Excel(name = "Real Name", width = 15) private String realname; /** Avatar */ @Excel(name = "Avatar", width = 15) private String avatar; /** Birthday */ @Excel(name = "Birthday", width = 15, format = "yyyy-MM-dd") private Date birthday; /** Gender (1: Male 2: Female) */ @Excel(name = "Gender", width = 15, dicCode="sex") private Integer sex; /** Email */ @Excel(name = "Email", width = 15) private String email; /** Phone */ @Excel(name = "Phone", width = 15) private String phone; /** Status (1: Normal 2: Frozen) */ @Excel(name = "Status", width = 15, replace={"Normal_1","Frozen_0"}) private Integer status; } ``` **One-to-Many Export Entity Example** ```Java @Data public class JeecgOrderMainPage { /** Primary Key */ private java.lang.String id; /** Order Number */ @Excel(name="Order Number", width=15) private java.lang.String orderCode; /** Order Type */ private java.lang.String ctype; /** Order Date */ @Excel(name="Order Date", width=15, format = "yyyy-MM-dd") private java.util.Date orderDate; /** Order Amount */ @Excel(name="Order Amount", width=15) private java.lang.Double orderMoney; /** Order Note */ private java.lang.String content; /** Created By */ private java.lang.String createBy; /** Create Time */ private java.util.Date createTime; /** Updated By */ private java.lang.String updateBy; /** Update Time */ private java.util.Date updateTime; @ExcelCollection(name="Customer") private List jeecgOrderCustomerList; @ExcelCollection(name="Ticket") private List jeecgOrderTicketList; } ``` --------------------------- Example Code --------------------------- - [Example Code](./autopoi-spring-boot-2-starter/src/test/java/) - Unit test code location --------------------------- License --------------------------- Apache License 2.0 ================================================ FILE: README.zh-CN.md ================================================ 中文 | [English](./README.md) AutoPOI (Excel和 Word简易工具类) =========================== AutoPOI 功能如同名字auto,追求的就是自动化,让一个没接触过poi的人员,可以傻瓜化的快速实现Excel导入导出、Word模板导出、可以仅仅5行代码就可以完成Excel的导入导出。 当前最新版本: 2.0.4(发布日期:2025-12-21) --------------------------- AutoPOI的主要特点 -------------------------- 1.设计精巧,使用简单 2.接口丰富,扩展简单 3.默认值多,write less do more 4.AbstractView 支持,web导出可以简单明了 --------------------------- AutoPOI的几个入口工具类 --------------------------- 1.ExcelExportUtil Excel导出(普通导出,模板导出) 2.ExcelImportUtil Excel导入 3.WordExportUtil Word导出(只支持docx ,doc版本poi存在图片的bug,暂不支持) --------------------------- 关于Excel导出XLS和XLSX区别 --------------------------- 1.导出时间XLS比XLSX快2-3倍 2.导出大小XLS是XLSX的2-3倍或者更多 3.导出需要综合网速和本地速度做考虑^~^ --------------------------- 几个工程的说明 --------------------------- 1.autopoi-parent 父包--作用大家都懂得 2.autopoi 导入导出的工具包,可以完成Excel导出,导入,Word的导出,Excel的导出功能 3.autopoi-spring-boot-2-starter Spring Boot 2.x 支持(兼容 javax.servlet) 4.autopoi-spring-boot-3-starter Spring Boot 3.x 支持(兼容 jakarta.servlet) 5.sax 导入使用xercesImpl这个包(这个包可能造成奇怪的问题哈),word导出使用poi-scratchpad,都作为可选包了 -------------------------- Maven 依赖配置 -------------------------- **Spring Boot 2.x 项目:** ```xml org.jeecgframework autopoi-spring-boot-2-starter 2.0.4 ``` **Spring Boot 3.x 项目:** ```xml org.jeecgframework autopoi-spring-boot-3-starter 2.0.4 ``` **纯 Java 项目(无 Spring):** ```xml org.jeecgframework autopoi 2.0.4 ``` -------------------------- AutoPoi 模板 表达式支持 -------------------------- - 空格分割 - 三目运算 {{test ? obj:obj2}} - n: 表示 这个cell是数值类型 {{n:}} - le: 代表长度{{le:()}} 在if/else 运用{{le:() > 8 ? obj1 : obj2}} - fd: 格式化时间 {{fd:(obj;yyyy-MM-dd)}} - fn: 格式化数字 {{fn:(obj;###.00)}} - fe: 遍历数据,创建row - !fe: 遍历数据不创建row - $fe: 下移插入,把当前行,下面的行全部下移.size()行,然后插入 - !if: 删除当前列 {{!if:(test)}} - 单引号表示常量值 '' 比如'1' 那么输出的就是 1 --------------------------- AutoPoi导出实例 --------------------------- 1.注解,导入导出都是基于注解的,实体上做上注解,标示导出对象,同时可以做一些操作 ```Java @ExcelTarget("courseEntity") public class CourseEntity implements java.io.Serializable { /** 主键 */ private String id; /** 课程名称 */ @Excel(name = "课程名称", orderNum = "1", needMerge = true) private String name; /** 老师主键 */ @ExcelEntity(id = "yuwen") @ExcelVerify() private TeacherEntity teacher; /** 老师主键 */ @ExcelEntity(id = "shuxue") private TeacherEntity shuxueteacher; @ExcelCollection(name = "选课学生", orderNum = "4") private List students; ``` 2.基础导出 传入导出参数,导出对象,以及对象列表即可完成导出 ```Java HSSFWorkbook workbook = ExcelExportUtil.exportExcel(new ExportParams( "2412312", "测试", "测试"), CourseEntity.class, list); ``` 3.基础导出,带有索引 在到处参数设置一个值,就可以在导出列增加索引 ```Java ExportParams params = new ExportParams("2412312", "测试", "测试"); params.setAddIndex(true); HSSFWorkbook workbook = ExcelExportUtil.exportExcel(params, TeacherEntity.class, telist); ``` 4.导出Map 创建类似注解的集合,即可完成Map的导出,略有麻烦 ```Java List entity = new ArrayList(); entity.add(new ExcelExportEntity("姓名", "name")); entity.add(new ExcelExportEntity("性别", "sex")); List> list = new ArrayList>(); Map map; for (int i = 0; i < 10; i++) { map = new HashMap(); map.put("name", "1" + i); map.put("sex", "2" + i); list.add(map); } HSSFWorkbook workbook = ExcelExportUtil.exportExcel(new ExportParams( "测试", "测试"), entity, list); ``` 5.模板导出 根据模板配置,完成对应导出 ```Java TemplateExportParams params = new TemplateExportParams(); params.setHeadingRows(2); params.setHeadingStartRow(2); Map map = new HashMap(); map.put("year", "2013"); map.put("sunCourses", list.size()); Map obj = new HashMap(); map.put("obj", obj); obj.put("name", list.size()); params.setTemplateUrl("org/jeecgframework/poi/excel/doc/exportTemp.xls"); Workbook book = ExcelExportUtil.exportExcel(params, CourseEntity.class, list, map); ``` 6.导入 设置导入参数,传入文件或者流,即可获得相应的list ```Java ImportParams params = new ImportParams(); params.setTitleRows(2); params.setHeadRows(2); //params.setSheetNum(9); params.setNeedSave(true); long start = new Date().getTime(); List list = ExcelImportUtil.importExcel(new File( "d:/tt.xls"), CourseEntity.class, params); ``` 7.SpringMvc的无缝融合 简单几句话,Excel导出搞定 ```Java @RequestMapping(value = "/exportXls") public ModelAndView exportXls(HttpServletRequest request, HttpServletResponse response) { ModelAndView mv = new ModelAndView(new JeecgEntityExcelView()); List pageList = jeecgDemoService.list(); //导出文件名称 mv.addObject(NormalExcelConstants.FILE_NAME,"导出Excel文件名字"); //注解对象Class mv.addObject(NormalExcelConstants.CLASS,JeecgDemo.class); //自定义表格参数 mv.addObject(NormalExcelConstants.PARAMS,new ExportParams("自定义导出Excel模板内容标题", "自定义Sheet名字")); //导出数据列表 mv.addObject(NormalExcelConstants.DATA_LIST,pageList); return mv; } ``` | 自定义视图 | 用途 | 描述 | | ------ | ------ | ------ | | JeecgMapExcelView | 实体对象导出视图 | 例如:List | | JeecgEntityExcelView | Map对象导出视图 | List> list | | JeecgTemplateExcelView | Excel模板导出视图 | - | | JeecgTemplateWordView | Word模板导出视图 | - | 8.Excel导入校验,过滤不符合规则的数据,追加错误信息到Excel,提供常用的校验规则,已经通用的校验接口 ```Java /** * Email校验 */ @Excel(name = "Email", width = 25) @ExcelVerify(isEmail = true, notNull = true) private String email; /** * 手机号校验 */ @Excel(name = "Mobile", width = 20) @ExcelVerify(isMobile = true, notNull = true) private String mobile; ExcelImportResult result = ExcelImportUtil.importExcelVerify(new File( "d:/tt.xls"), ExcelVerifyEntity.class, params); for (int i = 0; i < result.getList().size(); i++) { System.out.println(ReflectionToStringBuilder.toString(result.getList().get(i))); } ``` 9.导入Map 设置导入参数,传入文件或者流,即可获得相应的list,自定义Key,需要实现IExcelDataHandler接口 ```Java ImportParams params = new ImportParams(); List> list = ExcelImportUtil.importExcel(new File( "d:/tt.xls"), Map.class, params); ``` 10.字典用法 在实体属性注解excel中添加dicCode="",此处dicCode即为jeecg系统中数据字典的Code ```Java @Excel(name="性别",width=15,dicCode="sex") private java.lang.String sex; ``` 11.字典表用法 此处dictTable为数据库表名,dicCode为关联字段名,dicText为excel中显示的内容对应的字段 ```Java @Excel(name="部门",dictTable="t_s_depart",dicCode="id",dicText="departname") private java.lang.String depart; ``` 12.Replace用法 若数据库中存储的是0/1 ,则导出/导入的excel单元格中显示的是女/男 ```Java @Excel(name="测试替换",width=15,replace={"男_1","女_0"}) private java.lang.String fdReplace; ``` 13.高级字段转换用法 - exportConvert:在导出的时候需要替换值则配置该值为true,同时增加一个方法,方法名为原get方法名前加convert。 - importConvert:在导入的时候需要替换值则配置该值为true,同时增加一个方法,方法名为原set方法名前加convert。 ```Java @Excel(name="测试转换",width=15,exportConvert=true,importConvert=true) private java.lang.String fdConvert; /** * 转换值示例: 在该字段值的后面加上元 * @return */ public String convertgetFdConvert(){ return this.fdConvert+"元"; } /** * 转换值示例: 替换掉excel单元格中的"元" * @return */ public void convertsetFdConvert(String fdConvert){ this.fdConvert = fdConvert.replace("元",""); } ``` --------------------------- Excel 注解说明 --------------------------- @Excel | 属性 | 类型 | 默认值 | 功能 | |----------------|----------|------------------|------------------------------------------------------------------------| | name | String | null | 列名,支持name_id | | needMerge | boolean | fasle | 是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row) | | orderNum | String | "0" | 列的排序,支持name_id | | replace | String[] | {} | 值得替换 导出是{a_id,b_id} 导入反过来 | | savePath | String | "upload" | 导入文件保存路径,如果是图片可以填写,默认是upload/className/ IconEntity这个类对应的就是upload/Icon/ | | type | int | 1 | 导出类型 1 是文本 2 是图片,3 是函数,10 是数字 默认是文本 | | width | double | 10 | 列宽 | | height | double | 10 | 列高,后期打算统一使用@ExcelTarget的height,这个会被废弃,注意 | | isStatistics | boolean | fasle | 自动统计数据,在追加一行统计,把所有数据都和输出 这个处理会吞没异常,请注意这一点 | | isHyperlink | boolean | FALSE | 超链接,如果是需要实现接口返回对象 | | isImportField | boolean | TRUE | 校验字段,看看这个字段是不是导入的Excel中有,如果没有说明是错误的Excel,读取失败,支持name_id | | exportFormat | String | "" | 导出的时间格式,以这个是否为空来判断是否需要格式化日期 | | importFormat | String | "" | 导入的时间格式,以这个是否为空来判断是否需要格式化日期 | | format | String | "" | 时间格式,相当于同时设置了exportFormat 和 importFormat | | databaseFormat | String | "yyyyMMddHHmmss" | 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式,用以转换时间格式输出 | | numFormat | String | "" | 数字格式化,参数是Pattern,使用的对象是DecimalFormat | | imageType | int | 1 | 导出类型 1 从file读取 2 是从数据库中读取 默认是文件 同样导入也是一样的 | | suffix | String | "" | 文字后缀,如% 90 变成90% | | isWrap | boolean | TRUE | 是否换行 即支持\n | | mergeRely | int[] | {} | 合并单元格依赖关系,比如第二列合并是基于第一列 则{0}就可以了 | | mergeVertical | boolean | fasle | 纵向合并内容相同的单元格 | | fixedIndex | int | -1 | 对应excel的列,忽略名字 | | isColumnHidden | boolean | FALSE | 导出隐藏列 | @ExcelCollection | 属性 | 类型 | 默认值 | 功能 | |----------|----------|-----------------|------------------| | id | String | null | 定义ID | | name | String | null | 定义集合列名,支持nanm_id | | orderNum | int | 0 | 排序,支持name_id | | type | Class | ArrayList.class | 导入时创建对象使用 | 单表导出实体注解源码 ```Java public class SysUser implements Serializable { /**id*/ private String id; /**登录账号 */ @Excel(name = "登录账号", width = 15) private String username; /**真实姓名*/ @Excel(name = "真实姓名", width = 15) private String realname; /**头像*/ @Excel(name = "头像", width = 15) private String avatar; /**生日*/ @Excel(name = "生日", width = 15, format = "yyyy-MM-dd") private Date birthday; /**性别(1:男 2:女)*/ @Excel(name = "性别", width = 15,dicCode="sex") private Integer sex; /**电子邮件*/ @Excel(name = "电子邮件", width = 15) private String email; /**电话*/ @Excel(name = "电话", width = 15) private String phone; /**状态(1:正常 2:冻结 )*/ @Excel(name = "状态", width = 15,replace={"正常_1","冻结_0"}) private Integer status; ``` 一对多导出实体注解源码 ```Java @Data public class JeecgOrderMainPage { /**主键*/ private java.lang.String id; /**订单号*/ @Excel(name="订单号",width=15) private java.lang.String orderCode; /**订单类型*/ private java.lang.String ctype; /**订单日期*/ @Excel(name="订单日期",width=15,format = "yyyy-MM-dd") private java.util.Date orderDate; /**订单金额*/ @Excel(name="订单金额",width=15) private java.lang.Double orderMoney; /**订单备注*/ private java.lang.String content; /**创建人*/ private java.lang.String createBy; /**创建时间*/ private java.util.Date createTime; /**修改人*/ private java.lang.String updateBy; /**修改时间*/ private java.util.Date updateTime; @ExcelCollection(name="客户") private List jeecgOrderCustomerList; @ExcelCollection(name="机票") private List jeecgOrderTicketList; } ``` ================================================ FILE: autopoi/pom.xml ================================================ 4.0.0 org.jeecgframework autopoi-parent 2.0.4 autopoi autopoi 1.8 1.8 1.8 org.apache.poi poi org.apache.poi poi-ooxml commons-io commons-io xml-apis xml-apis commons-codec commons-codec xerces xercesImpl true org.apache.poi poi-scratchpad com.google.guava guava org.apache.commons commons-lang3 commons-io commons-io org.slf4j slf4j-api true org.springframework spring-webmvc true junit junit 4.13.2 test org.apache.logging.log4j log4j-to-slf4j 2.24.3 test org.apache.logging.log4j log4j-api 2.24.3 test org.apache.maven.plugins maven-compiler-plugin 3.11.0 1.8 1.8 8 UTF-8 org.apache.maven.plugins maven-surefire-plugin 3.0.0-M5 false ================================================ FILE: autopoi/src/main/java/org/jeecgframework/core/util/ApplicationContextUtil.java ================================================ package org.jeecgframework.core.util; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** * * @author 张代浩 * */ @Lazy(false) @Component public class ApplicationContextUtil implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext context) throws BeansException { ApplicationContextUtil.context = context; } public static ApplicationContext getContext() { return context; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/dict/service/AutoPoiDictServiceI.java ================================================ package org.jeecgframework.dict.service; /** * 描述: * @author:scott * @since:2017-4-12 下午04:58:15 * @version:1.0 */ public interface AutoPoiDictServiceI{ /** * 方法描述: 查询数据字典 * 作 者: yiming.zhang * 日 期: 2014年5月11日-下午4:22:42 * @param dicTable * @param dicCode * @param dicText * @return * 返回类型: List */ public String[] queryDict(String dicTable,String dicCode, String dicText); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/cache/ExcelCache.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.cache; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.jeecgframework.poi.cache.manager.POICacheManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.FileInputStream; import java.io.InputStream; import java.util.Arrays; import java.util.List; /** * Excel类型的缓存 * * @author JEECG * @date 2014年2月11日 * @version 1.0 */ public final class ExcelCache { private static final Logger LOGGER = LoggerFactory.getLogger(ExcelCache.class); public static Workbook getWorkbook(String url, Integer[] sheetNums, boolean needAll) { InputStream is = null; List sheetList = Arrays.asList(sheetNums); try { is = POICacheManager.getFile(url); Workbook wb = WorkbookFactory.create(is); // 删除其他的sheet if (!needAll) { for (int i = wb.getNumberOfSheets() - 1; i >= 0; i--) { if (!sheetList.contains(i)) { wb.removeSheetAt(i); } } } return wb; } catch (Exception e) { LOGGER.error(e.getMessage(), e); } finally { try { is.close(); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } return null; } //update-begin-author:wangshuai date:20200730 for:jar 包上传到服务器后 autopoi 读取不到excel模版文件 #1505 public static Workbook getWorkbookByTemplate(String url, Integer[] sheetNums, boolean needAll) { List sheetList = Arrays.asList(sheetNums); InputStream fis = null; try { //update-begin----author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作-------- //ClassPathResource resource = new ClassPathResource(url); fis = new FileInputStream(url); LOGGER.info(" >>> poi3升级到4兼容改造工作, url="+url); //fis = resource.getInputStream(); //update-end-----author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作-------- Workbook wb = WorkbookFactory.create(fis); // 删除其他的sheet if (!needAll) { for (int i = wb.getNumberOfSheets() - 1; i >= 0; i--) { if (!sheetList.contains(i)) { wb.removeSheetAt(i); } } } return wb; } catch (Exception e) { LOGGER.error(e.getMessage(), e); } finally { try { fis.close(); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } return null; } //update-end-author:wangshuai date:20200730 for:jar 包上传到服务器后 autopoi 读取不到excel模版文件 #1505 } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/cache/ImageCache.java ================================================ /** * Copyright 2013-2015 JueYue (qrb.jueyue@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 org.jeecgframework.poi.cache; import org.apache.poi.util.IOUtils; import org.jeecgframework.poi.cache.manager.POICacheManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.InputStream; /** * 图片缓存处理 * * @author liusq * 2022-05-27 下午4:16:32 */ public class ImageCache { private static final Logger LOGGER = LoggerFactory .getLogger(ImageCache.class); public static byte[] getImage(String imagePath) { InputStream is = POICacheManager.getFile(imagePath); ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); final ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); try { int ch; while ((ch = is.read()) != -1) { swapStream.write(ch); } Image image = Toolkit.getDefaultToolkit().createImage(swapStream.toByteArray()); BufferedImage bufferImg = toBufferedImage(image); ImageIO.write(bufferImg, imagePath.substring(imagePath.lastIndexOf(".") + 1, imagePath.length()), byteArrayOut); return byteArrayOut.toByteArray(); } catch (Exception e) { LOGGER.error(e.getMessage(), e); return null; } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(swapStream); IOUtils.closeQuietly(byteArrayOut); } } public static BufferedImage toBufferedImage(Image image) { if (image instanceof BufferedImage) { return (BufferedImage) image; } // This code ensures that all the pixels in the image are loaded image = new ImageIcon(image).getImage(); BufferedImage bimage = null; GraphicsEnvironment ge = GraphicsEnvironment .getLocalGraphicsEnvironment(); try { int transparency = Transparency.OPAQUE; GraphicsDevice gs = ge.getDefaultScreenDevice(); GraphicsConfiguration gc = gs.getDefaultConfiguration(); bimage = gc.createCompatibleImage(image.getWidth(null), image.getHeight(null), transparency); } catch (HeadlessException e) { // The system does not have a screen } if (bimage == null) { // Create a buffered image using the default color model int type = BufferedImage.TYPE_INT_RGB; bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type); } // Copy image to buffered image Graphics g = bimage.createGraphics(); // Paint the image onto the buffered image g.drawImage(image, 0, 0, null); g.dispose(); return bimage; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/cache/WordCache.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.cache; import java.io.InputStream; import org.jeecgframework.poi.cache.manager.POICacheManager; import org.jeecgframework.poi.word.entity.MyXWPFDocument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * word 缓存中心 * * @author JEECG * @date 2014年7月24日 下午10:54:31 */ public class WordCache { private static final Logger LOGGER = LoggerFactory.getLogger(WordCache.class); public static MyXWPFDocument getXWPFDocumen(String url) { InputStream is = null; try { is = POICacheManager.getFile(url); MyXWPFDocument doc = new MyXWPFDocument(is); return doc; } catch (Exception e) { LOGGER.error(e.getMessage(), e); } finally { try { is.close(); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } return null; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/cache/manager/FileLoade.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.cache.manager; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import org.jeecgframework.poi.util.PoiPublicUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 文件加载类,根据路径加载指定文件 * * @author JEECG * @date 2014年2月10日 * @version 1.0 */ class FileLoade { private static final Logger LOGGER = LoggerFactory.getLogger(FileLoade.class); public byte[] getFile(String url) { FileInputStream fileis = null; ByteArrayOutputStream baos = null; try { // 先用绝对路径查询,再查询相对路径 try { fileis = new FileInputStream(url); } catch (FileNotFoundException e) { String path = PoiPublicUtil.getWebRootPath(url); fileis = new FileInputStream(path); } baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = fileis.read(buffer)) > -1) { baos.write(buffer, 0, len); } baos.flush(); return baos.toByteArray(); } catch (FileNotFoundException e) { LOGGER.error(e.getMessage(), e); } catch (IOException e) { LOGGER.error(e.getMessage(), e); } finally { try { if (fileis != null) fileis.close(); if (fileis != null) baos.close(); } catch (IOException e) { LOGGER.error(e.getMessage(), e); } } LOGGER.error(fileis + "这个路径文件没有找到,请查询"); return null; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/cache/manager/POICacheManager.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.cache.manager; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.Arrays; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; /** * 缓存管理 * * @author JEECG * @date 2014年2月10日 * @version 1.0 */ public final class POICacheManager { private static final Logger LOGGER = LoggerFactory.getLogger(POICacheManager.class); private static LoadingCache loadingCache; static { loadingCache = CacheBuilder.newBuilder().expireAfterWrite(7, TimeUnit.DAYS).maximumSize(50).build(new CacheLoader() { @Override public byte[] load(String url) throws Exception { return new FileLoade().getFile(url); } }); } public static InputStream getFile(String id) { try { // 复杂数据,防止操作原数据 byte[] result = Arrays.copyOf(loadingCache.get(id), loadingCache.get(id).length); return new ByteArrayInputStream(result); } catch (ExecutionException e) { LOGGER.error(e.getMessage(), e); } return null; } //update-begin---author:chenrui ---date:20240403 for:[issue/#5933]增加清除缓存方法------------ /** * 清除所有缓存 * * @author chenrui * @date 2024/4/3 11:46 */ public static void cleanAll() { loadingCache.invalidateAll(); } /** * 清除缓存 * * @param id 缓存key * @author chenrui * @date 2024/4/3 11:46 */ public static void clean(String id) { loadingCache.invalidate(id); } //update-end---author:chenrui ---date:20240403 for:[issue/#5933]增加清除缓存方法------------ } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/cache/package-info.java ================================================ /** * 对POI用到的模板进行缓存,进行统一管理,缓存工具暂时使用guava(脱离配置文件) * 缓存方式统一为byte[] 屏蔽文件类型的差异 * 缓存获取方式,URL或者URL+index(EXcel的) */ /** * @author JEECG * @date 2014年2月10日 * @version 1.0 */ package org.jeecgframework.poi.cache; ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/consts/ImageScaleMode.java ================================================ package org.jeecgframework.poi.consts; /** * 图片缩放模式枚举 * for [issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式 * * @author chenrui * @date 2025-10-29 */ public enum ImageScaleMode { /** * 拉伸填充 */ STRETCH(0, "拉伸填充"), /** * 等比例缩放适应 */ FIT(1, "等比例缩放适应"), /** * 不缩放(原始大小) */ ORIGINAL(2, "不缩放(原始大小)"); private final int code; private final String description; ImageScaleMode(int code, String description) { this.code = code; this.description = description; } public int getCode() { return code; } public String getDescription() { return description; } /** * 根据code获取枚举 * * @param code 代码值 * @return 对应的枚举值,如果找不到则返回STRETCH */ public static ImageScaleMode valueOf(int code) { for (ImageScaleMode mode : values()) { if (mode.code == code) { return mode; } } return STRETCH; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/entity/ImageEntity.java ================================================ package org.jeecgframework.poi.entity; import org.jeecgframework.poi.consts.ImageScaleMode; /** * word导出,图片设置和图片信息 * * @author liusq * @date 2022-5-27 */ public class ImageEntity { public static String URL = "url"; public static String Data = "data"; /** * 图片输入方式 */ private String type = URL; /** * 图片宽度 */ private int width; // 图片高度 private int height; // 图片地址 private String url; // 图片信息 private byte[] data; private int rowspan = 1; private int colspan = 1; /** * 图片缩放模式枚举 * for [issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式 */ private ImageScaleMode scaleModeEnum = ImageScaleMode.STRETCH; public ImageEntity() { } public ImageEntity(byte[] data, int width, int height) { this.data = data; this.width = width; this.height = height; this.type = Data; } public ImageEntity(String url, int width, int height) { this.url = url; this.width = width; this.height = height; } public byte[] getData() { return data; } public int getHeight() { return height; } public String getType() { return type; } public String getUrl() { return url; } public int getWidth() { return width; } public void setData(byte[] data) { this.data = data; } public void setHeight(int height) { this.height = height; } public void setType(String type) { this.type = type; } public void setUrl(String url) { this.url = url; } public void setWidth(int width) { this.width = width; } public int getRowspan() { return rowspan; } public void setRowspan(int rowspan) { this.rowspan = rowspan; } public int getColspan() { return colspan; } public void setColspan(int colspan) { this.colspan = colspan; } /** * 获取图片缩放模式 * @deprecated 使用 {@link #getScaleModeEnum()} 代替 * @return 缩放模式代码值 */ @Deprecated public int getScaleMode() { return scaleModeEnum.getCode(); } /** * 设置图片缩放模式 * @deprecated 使用 {@link #setScaleModeEnum(ImageScaleMode)} 代替 * @param scaleMode 缩放模式代码值 */ @Deprecated public void setScaleMode(int scaleMode) { this.scaleModeEnum = ImageScaleMode.valueOf(scaleMode); } /** * 获取图片缩放模式枚举 * @return 缩放模式枚举 */ public ImageScaleMode getScaleModeEnum() { return scaleModeEnum; } /** * 设置图片缩放模式枚举 * @param scaleModeEnum 缩放模式枚举 */ public void setScaleModeEnum(ImageScaleMode scaleModeEnum) { this.scaleModeEnum = scaleModeEnum; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelExportUtil.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.entity.TemplateExportParams; import org.jeecgframework.poi.excel.entity.enmus.ExcelType; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.jeecgframework.poi.excel.export.ExcelBatchExportServer; import org.jeecgframework.poi.excel.export.ExcelExportServer; import org.jeecgframework.poi.excel.export.template.ExcelExportOfTemplateUtil; import org.jeecgframework.poi.handler.inter.IExcelExportServer; import org.jeecgframework.poi.handler.inter.IExcelExportServerEnhanced; import org.jeecgframework.poi.handler.inter.IWriter; /** * excel 导出工具类 * * @author JEECG * @version 1.0 * @date 2013-10-17 */ //update-begin---author:chenrui ---date:20231221 for:[issue/#5248]加强继承扩展便利性(删除final)------------ public class ExcelExportUtil { //update-end---author:chenrui ---date:20231221 for:[issue/#5248]加强继承扩展便利性(删除final)------------ //update-begin---author:liusq Date:20211217 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- //单sheet最大值 public static int USE_SXSSF_LIMIT = 100000; //update-end---author:liusq Date:20211217 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- private ExcelExportUtil() { } //---update-begin-----autor:scott------date:20191016-------for:导出字段支持自定义-------- /** * 根据Entity创建对应的Excel * * @param entity * 表格标题属性 * @param pojoClass * Excel对象Class * @param dataSet * Excel对象数据List * @param exportFields * 自定义导出Excel字段数组 */ public static Workbook exportExcel(ExportParams entity, Class pojoClass, Collection dataSet,String[] exportFields) { Workbook workbook; if (ExcelType.HSSF.equals(entity.getType())) { workbook = new HSSFWorkbook(); } else if (dataSet.size() < 1000) { workbook = new XSSFWorkbook(); } else { workbook = new SXSSFWorkbook(); } new ExcelExportServer().createSheet(workbook, entity, pojoClass, dataSet,exportFields); return workbook; } //---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义-------- /** * 根据Entity创建对应的Excel * * @param entity * 表格标题属性 * @param pojoClass * Excel对象Class * @param dataSet * Excel对象数据List */ public static Workbook exportExcel(ExportParams entity, Class pojoClass, Collection dataSet) { Workbook workbook; if (ExcelType.HSSF.equals(entity.getType())) { workbook = new HSSFWorkbook(); } else if (dataSet.size() < 1000) { workbook = new XSSFWorkbook(); } else { workbook = new SXSSFWorkbook(); } new ExcelExportServer().createSheet(workbook, entity, pojoClass, dataSet,null); return workbook; } /** * 根据Map创建对应的Excel * * @param entity * 表格标题属性 * @param pojoClass * Excel对象Class * @param dataSet * Excel对象数据List */ public static Workbook exportExcel(ExportParams entity, List entityList, Collection> dataSet) { Workbook workbook; if (ExcelType.HSSF.equals(entity.getType())) { workbook = new HSSFWorkbook(); } else if (dataSet.size() < 1000) { workbook = new XSSFWorkbook(); } else { workbook = new SXSSFWorkbook(); } new ExcelExportServer().createSheetForMap(workbook, entity, entityList, dataSet); return workbook; } /** * 一个excel 创建多个sheet * * @param list * 多个Map key title 对应表格Title key entity 对应表格对应实体 key data * Collection 数据 * @return */ public static Workbook exportExcel(List> list, ExcelType type) { Workbook workbook; if (ExcelType.HSSF.equals(type)) { workbook = new HSSFWorkbook(); } else { workbook = new XSSFWorkbook(); } for (Map map : list) { ExcelExportServer server = new ExcelExportServer(); server.createSheet(workbook, (ExportParams) map.get("title"), (Class) map.get("entity"), (Collection) map.get("data"),null); } return workbook; } /** * 导出文件通过模板解析,不推荐这个了,推荐全部通过模板来执行处理 * * @param params * 导出参数类 * @param pojoClass * 对应实体 * @param dataSet * 实体集合 * @param map * * 模板集合 * @return */ public static Workbook exportExcel(TemplateExportParams params, Class pojoClass, Collection dataSet, Map map) { return new ExcelExportOfTemplateUtil().createExcleByTemplate(params, pojoClass, dataSet, map); } /** * 导出文件通过模板解析只有模板,没有集合 * * @param params * 导出参数类 * @param map * 模板集合 * @return */ public static Workbook exportExcel(TemplateExportParams params, Map map) { return new ExcelExportOfTemplateUtil().createExcleByTemplate(params, null, null, map); } //update-begin---author:liusq Date:20211227 for:[LOWCOD-2521]大数据导出方法【全局】---- /** * 大数据量导出 * * @param entity 表格标题属性 * @param pojoClass Excel对象Class * @date 2022年1月4号 * @return ExcelBatchExportServer 批处理服务 */ public static IWriter exportBigExcel(ExportParams entity, Class pojoClass) { ExcelBatchExportServer batchServer = new ExcelBatchExportServer(); batchServer.init(entity, pojoClass); return batchServer; } /** * 大数据量导出 * * @param entity * @param excelParams * @date 2022年1月4号 * @return ExcelBatchExportServer 批处理服务 */ public static IWriter exportBigExcel(ExportParams entity, List excelParams) { ExcelBatchExportServer batchServer = new ExcelBatchExportServer(); batchServer.init(entity, excelParams); return batchServer; } /** * 大数据量导出 * * @param entity 导出参数属性 * @param pojoClass Excel对象Class * @param server 查询数据的接口 * @param queryParams 查询数据的参数 * @date 2022年1月4号 * @return Workbook */ public static Workbook exportBigExcel(ExportParams entity, Class pojoClass, IExcelExportServer server, Object queryParams) { ExcelBatchExportServer batchServer = new ExcelBatchExportServer(); batchServer.init(entity, pojoClass); return batchServer.exportBigExcel(server, queryParams); } /** * 大数据量导出 * @param entity * @param excelParams * @param server 查询数据的接口 * @param queryParams 查询数据的参数 * @date 2022年1月4号 * @return Workbook */ public static Workbook exportBigExcel(ExportParams entity, List excelParams, IExcelExportServer server, Object queryParams) { ExcelBatchExportServer batchServer = new ExcelBatchExportServer(); batchServer.init(entity, excelParams); return batchServer.exportBigExcel(server, queryParams); } //update-end---author:liusq Date:20211227 for:[LOWCOD-2521]大数据导出方法【全局】---- //update-begin---author:chenrui Date:20251103 for:[issues/8892]解决40万+数据导出查询效率问题---- /** * 大数据量导出 - 高性能游标分页方案 * for [QQYUN-13964]演示系统数据量大,点击没反应 *

* 适用场景: * 1. 数据量超过40万+的导出场景 * 2. 需要避免深分页性能问题 * 3. 数据表有自增ID或其他有序字段 * * 性能对比: * - 传统方式: 40万数据,最后一页查询可能需要10+秒 * - 游标方式: 40万数据,每页查询时间恒定在0.1秒左右 * * 使用示例: *

	 * ExportParams params = new ExportParams("订单列表", "订单");
	 * IExcelExportServerEnhanced server = new IExcelExportServerEnhanced() {
	 *     public List selectListForExcelExport(Object queryParams, Object lastRecord, int pageSize) {
	 *         Long lastId = lastRecord != null ? ((Order)lastRecord).getId() : 0L;
	 *         return orderMapper.selectList(new QueryWrapper()
	 *             .gt("id", lastId)
	 *             .orderByAsc("id")
	 *             .last("LIMIT " + pageSize));
	 *     }
	 * };
	 * Workbook workbook = ExcelExportUtil.exportBigExcelEnhanced(params, Order.class, server, queryParams);
	 * 
	 * 
	 * @param entity      导出参数属性
	 * @param pojoClass   Excel对象Class
	 * @param server      增强的查询数据接口(支持游标分页)
	 * @param queryParams 查询数据的参数
	 * @date 2025年11月3号
	 * @return Workbook
	 */
	public static  Workbook exportBigExcelEnhanced(ExportParams entity, Class pojoClass,
												  IExcelExportServerEnhanced server, Object queryParams) {
		ExcelBatchExportServer batchServer = new ExcelBatchExportServer();
		batchServer.init(entity, pojoClass);
		return batchServer.exportBigExcelEnhanced(server, queryParams);
	}

	/**
	 * 大数据量导出 - 高性能游标分页方案(自定义列)
	 * 
	 * @param entity       导出参数属性
	 * @param excelParams  自定义导出列
	 * @param server       增强的查询数据接口(支持游标分页)
	 * @param queryParams  查询数据的参数
	 * @date 2025年11月3号
	 * @return Workbook
	 */
	public static  Workbook exportBigExcelEnhanced(ExportParams entity, List excelParams,
												  IExcelExportServerEnhanced server, Object queryParams) {
		ExcelBatchExportServer batchServer = new ExcelBatchExportServer();
		batchServer.init(entity, excelParams);
		return batchServer.exportBigExcelEnhanced(server, queryParams);
	}
	//update-end---author:chenrui  Date:20251103 for:[issues/8892]解决40万+数据导出查询效率问题----
}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelImportCheckUtil.java
================================================
package org.jeecgframework.poi.excel;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jeecgframework.core.util.ApplicationContextUtil;
import org.jeecgframework.dict.service.AutoPoiDictServiceI;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecgframework.poi.excel.annotation.ExcelCollection;
import org.jeecgframework.poi.excel.annotation.ExcelTarget;
import org.jeecgframework.poi.excel.annotation.ExcelVerify;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams;
import org.jeecgframework.poi.excel.entity.params.ExcelImportEntity;
import org.jeecgframework.poi.excel.entity.params.ExcelVerifyEntity;
import org.jeecgframework.poi.excel.imports.ExcelImportServer;
import org.jeecgframework.poi.exception.excel.ExcelImportException;
import org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum;
import org.jeecgframework.poi.util.ExcelUtil;
import org.jeecgframework.poi.util.PoiPublicUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.util.*;

/**
 * EXCEL INCLUE CHECK
 * 验证excel标题是否存在,当前默认有0.8(80%)即可通过验证
 */
public class ExcelImportCheckUtil {
    private final static Logger LOGGER = LoggerFactory.getLogger(ExcelImportCheckUtil.class);

    /**当有标题到达多少可以通过验证*/
    public static final Double defScreenRate = 0.8;

    /**
     * check inclue filed match rate
     * @param inputstream
     * @param pojoClass
     * @param params
     * @return
     */
    public static Boolean check(InputStream inputstream, Class pojoClass, ImportParams params) {
        return check(inputstream,pojoClass,params,defScreenRate);
    }
    /**
     * check inclue filed match rate
     * @param inputstream
     * @param pojoClass
     * @param params
     * @param screenRate field match rate (defalut:0.8)
     * @return
     */
    public static Boolean check(InputStream inputstream, Class pojoClass, ImportParams params, Double screenRate) {
        Workbook book = null;
        int errorNum = 0;
        int successNum = 0;
        if (!(inputstream.markSupported())) {
            inputstream = new PushbackInputStream(inputstream, 8);
        }
        try {
//            if (POIFSFileSystem.hasPOIFSHeader(inputstream)) {
//                book = new HSSFWorkbook(inputstream);
//            } else if (POIXMLDocument.hasOOXMLHeader(inputstream)) {
//                book = new XSSFWorkbook(OPCPackage.open(inputstream));
//            }
            book = WorkbookFactory.create(inputstream);
            LOGGER.info("  >>>  poi3升级到4兼容改造工作, pojoClass="+pojoClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (int i = 0; i < params.getSheetNum(); i++) {
            Row row = null;
            //跳过表头和标题行
            Iterator rows;
            try{
                rows=  book.getSheetAt(i).rowIterator();
            }catch (Exception e){
                //为空说明读取不到,故不是excel
                  throw new RuntimeException("请导入正确格式的excel文件!");
            }


            for (int j = 0; j < params.getTitleRows() + params.getHeadRows(); j++) {
                try{
                    row = rows.next();
                }catch (NoSuchElementException e){
                    //为空说明标题不出在,excel格式错误
                    throw new RuntimeException("请填写内容标题!");
                }
            }
            Sheet sheet = book.getSheetAt(i);
            Map titlemap = null;
            try {
                titlemap = getTitleMap(sheet, params);
            } catch (Exception e) {
                e.printStackTrace();
            }
            Set columnIndexSet = titlemap.keySet();
            Integer maxColumnIndex = Collections.max(columnIndexSet);
            Integer minColumnIndex = Collections.min(columnIndexSet);
            while (rows.hasNext() && (row == null || sheet.getLastRowNum() - row.getRowNum() > params.getLastOfInvalidRow())) {
                row = rows.next();
                Map excelParams = new HashMap();
                List excelCollection = new ArrayList();
                String targetId = null;
                if (!Map.class.equals(pojoClass)) {
                    Field fileds[] = PoiPublicUtil.getClassFields(pojoClass);
                    ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);
                    if (etarget != null) {
                        targetId = etarget.value();
                    }
                    try {
                        getAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                try {
                    int firstCellNum = row.getFirstCellNum();
                    if (firstCellNum > minColumnIndex) {
                        firstCellNum = minColumnIndex;
                    }
                    int lastCellNum = row.getLastCellNum();
                    if (lastCellNum < maxColumnIndex + 1) {
                        lastCellNum = maxColumnIndex + 1;
                    }
                    for (int j = firstCellNum, le = lastCellNum; j < le; j++) {
                        String titleString = (String) titlemap.get(j);
                        if (excelParams.containsKey(titleString) || Map.class.equals(pojoClass)) {
                            successNum+=1;
                        }else{
                            if(excelCollection.size()>0){
                                Iterator var33 = excelCollection.iterator();
                                ExcelCollectionParams param = (ExcelCollectionParams)var33.next();
                                if (param.getExcelParams().containsKey(titleString)) {
                                    successNum+=1;
                                }else{
                                    errorNum+=1;
                                }
                            }else{
                                errorNum+=1;
                            }
                        }
                    }
                    if(successNumerrorNum){
                        if(errorNum>0){
                            double newNumber = (double) successNum / (successNum + errorNum);
                            BigDecimal bg = new BigDecimal(newNumber);
                            double f1 = bg.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
                            if(f1 getTitleMap(Sheet sheet, ImportParams params) throws Exception {
        Map titlemap = new HashMap();
        Iterator cellTitle = null;
        String collectionName = null;
        Row headRow = null;
        int headBegin = params.getTitleRows();
        int allRowNum = sheet.getPhysicalNumberOfRows();
        while(headRow == null && headBegin < allRowNum){
            headRow = sheet.getRow(headBegin++);
        }
        if(headRow==null){
            throw new Exception("不识别该文件");
        }
        if (ExcelUtil.isMergedRegion(sheet, headRow.getRowNum(), 0)) {
            params.setHeadRows(2);
        }else{
            params.setHeadRows(1);
        }
        cellTitle = headRow.cellIterator();
        while (cellTitle.hasNext()) {
            Cell cell = cellTitle.next();
            String value = getKeyValue(cell);
            if (StringUtils.isNotEmpty(value)) {
                titlemap.put(cell.getColumnIndex(), value);//加入表头列表
            }
        }

        //多行表头
        for (int j = headBegin; j < headBegin + params.getHeadRows()-1; j++) {
            headRow = sheet.getRow(j);
            cellTitle = headRow.cellIterator();
            while (cellTitle.hasNext()) {
                Cell cell = cellTitle.next();
                String value = getKeyValue(cell);
                if (StringUtils.isNotEmpty(value)) {
                    int columnIndex = cell.getColumnIndex();
                    //当前cell的上一行是否为合并单元格
                    if(ExcelUtil.isMergedRegion(sheet, cell.getRowIndex()-1, columnIndex)){
                        collectionName = ExcelUtil.getMergedRegionValue(sheet, cell.getRowIndex()-1, columnIndex);
                        if(params.isIgnoreHeader(collectionName)){
                            titlemap.put(cell.getColumnIndex(), value);
                        }else{
                            titlemap.put(cell.getColumnIndex(), collectionName + "_" + value);
                        }
                    }else{
                        titlemap.put(cell.getColumnIndex(), value);
                    }
                }
            }
        }
        return titlemap;
    }
    /**
     * 获取key的值,针对不同类型获取不同的值
     *
     * @Author JEECG
     * @date 20201023
     * @param cell
     * @return
     */
    private static String getKeyValue(Cell cell) {
        if(cell==null){
            return null;
        }
        Object obj = null;
        switch (cell.getCellType()) {
            case STRING:
                obj = cell.getStringCellValue();
                break;
            case BOOLEAN:
                obj = cell.getBooleanCellValue();
                break;
            case NUMERIC:
                obj = cell.getNumericCellValue();
                break;
            case FORMULA:
                obj = cell.getCellFormula();
                break;
        }
        return obj == null ? null : obj.toString().trim();
    }

    /**
     * 获取需要导出的全部字段
     *
     *
     *
     * @param targetId
     *            目标ID
     * @param fields
     * @param excelCollection
     * @throws Exception
     */
    public static void getAllExcelField(String targetId, Field[] fields, Map excelParams, List excelCollection, Class pojoClass, List getMethods) throws Exception {
        ExcelImportEntity excelEntity = null;
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (PoiPublicUtil.isNotUserExcelUserThis(null, field, targetId)) {
                continue;
            }
            if (PoiPublicUtil.isCollection(field.getType())) {
                // 集合对象设置属性
                ExcelCollectionParams collection = new ExcelCollectionParams();
                collection.setName(field.getName());
                Map temp = new HashMap();
                ParameterizedType pt = (ParameterizedType)field.getGenericType();
                Class clz = (Class)pt.getActualTypeArguments()[0];
                collection.setType(clz);
                getExcelFieldList(targetId, PoiPublicUtil.getClassFields(clz), clz, temp, (List)null);
                collection.setExcelParams(temp);
                collection.setExcelName(((ExcelCollection)field.getAnnotation(ExcelCollection.class)).name());
                additionalCollectionName(collection);
                excelCollection.add(collection);
            } else if (PoiPublicUtil.isJavaClass(field)) {
                addEntityToMap(targetId, field, (ExcelImportEntity)excelEntity, pojoClass, getMethods, excelParams);
            } else {
                List newMethods = new ArrayList();
                if (getMethods != null) {
                    newMethods.addAll(getMethods);
                }
                newMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass));
                getAllExcelField(targetId, PoiPublicUtil.getClassFields(field.getType()), excelParams, excelCollection, field.getType(), newMethods);
            }
        }
    }
    public static void getExcelFieldList(String targetId, Field[] fields, Class pojoClass, Map temp, List getMethods) throws Exception {
        ExcelImportEntity excelEntity = null;
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (!PoiPublicUtil.isNotUserExcelUserThis((List)null, field, targetId)) {
                if (PoiPublicUtil.isJavaClass(field)) {
                    addEntityToMap(targetId, field, (ExcelImportEntity)excelEntity, pojoClass, getMethods, temp);
                } else {
                    List newMethods = new ArrayList();
                    if (getMethods != null) {
                        newMethods.addAll(getMethods);
                    }

                    newMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass, field.getType()));
                    getExcelFieldList(targetId, PoiPublicUtil.getClassFields(field.getType()), field.getType(), temp, newMethods);
                }
            }
        }
    }
    /**
     * 追加集合名称到前面
     *
     * @param collection
     */
    private static void additionalCollectionName(ExcelCollectionParams collection) {
        Set keys = new HashSet();
        keys.addAll(collection.getExcelParams().keySet());
        Iterator var3 = keys.iterator();

        while(var3.hasNext()) {
            String key = (String)var3.next();
            collection.getExcelParams().put(collection.getExcelName() + "_" + key, collection.getExcelParams().get(key));
            collection.getExcelParams().remove(key);
        }
    }
    /**
     * 把这个注解解析放到类型对象中
     *
     * @param targetId
     * @param field
     * @param excelEntity
     * @param pojoClass
     * @param getMethods
     * @param temp
     * @throws Exception
     */
    public static void addEntityToMap(String targetId, Field field, ExcelImportEntity excelEntity, Class pojoClass, List getMethods, Map temp) throws Exception {
        Excel excel = field.getAnnotation(Excel.class);
        excelEntity = new ExcelImportEntity();
        excelEntity.setType(excel.type());
        excelEntity.setSaveUrl(excel.savePath());
        excelEntity.setSaveType(excel.imageType());
        excelEntity.setReplace(excel.replace());
        excelEntity.setDatabaseFormat(excel.databaseFormat());
        excelEntity.setVerify(getImportVerify(field));
        excelEntity.setSuffix(excel.suffix());
        excelEntity.setNumFormat(excel.numFormat());
        excelEntity.setGroupName(excel.groupName());
        //update-begin-author:taoYan date:20180202 for:TASK #2067 【bug excel 问题】excel导入字典文本翻译问题
        excelEntity.setMultiReplace(excel.multiReplace());
        if(StringUtils.isNotEmpty(excel.dicCode())){
            AutoPoiDictServiceI jeecgDictService = null;
            try {
                jeecgDictService = ApplicationContextUtil.getContext().getBean(AutoPoiDictServiceI.class);
            } catch (Exception e) {
            }
            if(jeecgDictService!=null){
                String[] dictReplace = jeecgDictService.queryDict(excel.dictTable(), excel.dicCode(), excel.dicText());
                if(excelEntity.getReplace()!=null && dictReplace!=null && dictReplace.length!=0){
                    excelEntity.setReplace(dictReplace);
                }
            }
        }
        //update-end-author:taoYan date:20180202 for:TASK #2067 【bug excel 问题】excel导入字典文本翻译问题
        getExcelField(targetId, field, excelEntity, excel, pojoClass);
        if (getMethods != null) {
            List newMethods = new ArrayList();
            newMethods.addAll(getMethods);
            newMethods.add(excelEntity.getMethod());
            excelEntity.setMethods(newMethods);
        }
        temp.put(excelEntity.getName(), excelEntity);

    }
    public static void getExcelField(String targetId, Field field, ExcelImportEntity excelEntity, Excel excel, Class pojoClass) throws Exception {
        excelEntity.setName(getExcelName(excel.name(), targetId));
        String fieldname = field.getName();
        //update-begin-author:taoyan for:TASK #2798 【例子】导入扩展方法,支持自定义导入字段转换规则
        excelEntity.setMethod(PoiPublicUtil.getMethod(fieldname, pojoClass, field.getType(),excel.importConvert()));
        //update-end-author:taoyan for:TASK #2798 【例子】导入扩展方法,支持自定义导入字段转换规则
        if (StringUtils.isNotEmpty(excel.importFormat())) {
            excelEntity.setFormat(excel.importFormat());
        } else {
            excelEntity.setFormat(excel.format());
        }
    }
    /**
     * 判断在这个单元格显示的名称
     *
     * @param exportName
     * @param targetId
     * @return
     */
    public static String getExcelName(String exportName, String targetId) {
        if (exportName.indexOf("_") < 0) {
            return exportName;
        }
        String[] arr = exportName.split(",");
        for (String str : arr) {
            if (str.indexOf(targetId) != -1) {
                return str.split("_")[0];
            }
        }
        return null;
    }
    /**
     * 获取导入校验参数
     *
     * @param field
     * @return
     */
    public static ExcelVerifyEntity getImportVerify(Field field) {
        ExcelVerify verify = field.getAnnotation(ExcelVerify.class);
        if (verify != null) {
            ExcelVerifyEntity entity = new ExcelVerifyEntity();
            entity.setEmail(verify.isEmail());
            entity.setInterHandler(verify.interHandler());
            entity.setMaxLength(verify.maxLength());
            entity.setMinLength(verify.minLength());
            entity.setMobile(verify.isMobile());
            entity.setNotNull(verify.notNull());
            entity.setRegex(verify.regex());
            entity.setRegexTip(verify.regexTip());
            entity.setTel(verify.isTel());
            return entity;
        }
        return null;
    }
}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelImportUtil.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.entity.result.ExcelImportResult;
import org.jeecgframework.poi.excel.imports.ExcelImportServer;
import org.jeecgframework.poi.excel.imports.sax.SaxReadExcel;
import org.jeecgframework.poi.excel.imports.sax.parse.ISaxRowRead;
import org.jeecgframework.poi.handler.inter.IExcelReadRowHanlder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Excel 导入工具
 * 
 * @author JEECG
 * @date 2013-9-24
 * @version 1.0
 */
@SuppressWarnings({ "unchecked" })
public final class ExcelImportUtil {

	private ExcelImportUtil() {
	}

	private static final Logger LOGGER = LoggerFactory.getLogger(ExcelImportUtil.class);

	/**
	 * Excel 导入 数据源本地文件,不返回校验结果 导入 字 段类型 Integer,Long,Double,Date,String,Boolean
	 * 
	 * @param file
	 * @param pojoClass
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static  List importExcel(File file, Class pojoClass, ImportParams params) {
		FileInputStream in = null;
		List result = null;
		try {
			in = new FileInputStream(file);
			result = new ExcelImportServer().importExcelByIs(in, pojoClass, params).getList();
		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
		} finally {
			try {
				in.close();
			} catch (IOException e) {
				LOGGER.error(e.getMessage(), e);
			}
		}
		return result;
	}

	/**
	 * Excel 导入 数据源IO流,不返回校验结果 导入 字段类型 Integer,Long,Double,Date,String,Boolean
	 * 
	 * @param file
	 * @param pojoClass
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static  List importExcel(InputStream inputstream, Class pojoClass, ImportParams params) throws Exception {
		return new ExcelImportServer().importExcelByIs(inputstream, pojoClass, params).getList();
	}

	/**
	 * Excel 导入 数据源IO流,返回校验结果 字段类型 Integer,Long,Double,Date,String,Boolean
	 * 
	 * @param file
	 * @param pojoClass
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static  ExcelImportResult importExcelVerify(InputStream inputstream, Class pojoClass, ImportParams params) throws Exception {
		return new ExcelImportServer().importExcelByIs(inputstream, pojoClass, params);
	}

	/**
	 * Excel 导入 数据源本地文件,返回校验结果 字段类型 Integer,Long,Double,Date,String,Boolean
	 * 
	 * @param file
	 * @param pojoClass
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static  ExcelImportResult importExcelVerify(File file, Class pojoClass, ImportParams params) {
		FileInputStream in = null;
		try {
			in = new FileInputStream(file);
			return new ExcelImportServer().importExcelByIs(in, pojoClass, params);
		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
		} finally {
			try {
				in.close();
			} catch (IOException e) {
				LOGGER.error(e.getMessage(), e);
			}
		}
		return null;
	}

	/**
	 * Excel 通过SAX解析方法,适合大数据导入,不支持图片 导入 数据源IO流,不返回校验结果 导入 字段类型
	 * Integer,Long,Double,Date,String,Boolean
	 * 
	 * @param inputstream
	 * @param pojoClass
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static  List importExcelBySax(InputStream inputstream, Class pojoClass, ImportParams params) {
		return new SaxReadExcel().readExcel(inputstream, pojoClass, params, null, null);
	}

	/**
	 * Excel 通过SAX解析方法,适合大数据导入,不支持图片 导入 数据源本地文件,不返回校验结果 导入 字 段类型
	 * Integer,Long,Double,Date,String,Boolean
	 * 
	 * @param file
	 * @param rowRead
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	public static void importExcelBySax(InputStream inputstream, Class pojoClass, ImportParams params, IExcelReadRowHanlder hanlder) {
		new SaxReadExcel().readExcel(inputstream, pojoClass, params, null, hanlder);
	}

	/**
	 * Excel 通过SAX解析方法,适合大数据导入,不支持图片 导入 数据源IO流,不返回校验结果 导入 字段类型
	 * Integer,Long,Double,Date,String,Boolean
	 * 
	 * @param file
	 * @param rowRead
	 * @return
	 * @throws Exception
	 */
	public static  List importExcelBySax(InputStream inputstream, ISaxRowRead rowRead) {
		return new SaxReadExcel().readExcel(inputstream, null, null, rowRead, null);
	}

}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelToHtmlUtil.java
================================================
package org.jeecgframework.poi.excel;

import org.apache.poi.ss.usermodel.Workbook;
import org.jeecgframework.poi.excel.html.ExcelToHtmlServer;

/**
 * Excel 变成界面
 * 
 * @author JEECG
 * @date 2015年5月10日 上午11:51:48
 */
public final class ExcelToHtmlUtil {

	private ExcelToHtmlUtil() {
	}

	/**
	 * 转换成为Table
	 * 
	 * @param wb
	 *            Excel
	 * @return
	 */
	public static String toTableHtml(Workbook wb) {
		return new ExcelToHtmlServer(wb, false, 0).printPage();
	}

	/**
	 * 转换成为Table
	 * 
	 * @param wb
	 *            Excel
	 * @param sheetNum
	 *            sheetNum
	 * @return
	 */
	public static String toTableHtml(Workbook wb, int sheetNum) {
		return new ExcelToHtmlServer(wb, false, sheetNum).printPage();
	}

	/**
	 * 转换成为完整界面
	 * 
	 * @param wb
	 *            Excel
	 * @param sheetNum
	 *            sheetNum
	 * @return
	 */
	public static String toAllHtml(Workbook wb) {
		return new ExcelToHtmlServer(wb, true, 0).printPage();
	}

	/**
	 * 转换成为完整界面
	 * 
	 * @param wb
	 *            Excel
	 * @param sheetNum
	 *            sheetNum
	 * @return
	 */
	public static String toAllHtml(Workbook wb, int sheetNum) {
		return new ExcelToHtmlServer(wb, true, sheetNum).printPage();
	}

}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/Excel.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Inherited;

/**
 * Excel 导出基本注释
 * 
 * @author JEECG
 * @date 2014年6月20日 下午10:25:12
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
//update-begin---author:chenrui ---date:20231221  for:[issue/#5248]加强继承扩展便利性------------
@Inherited
//update-end---author:chenrui ---date:20231221  for:[issue/#5248]加强继承扩展便利性------------
public @interface Excel {

	/**
	 * 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式
	 */
	public String databaseFormat() default "yyyyMMddHHmmss";

	/**
	 * 导出的时间格式,以这个是否为空来判断是否需要格式化日期
	 */
	public String exportFormat() default "";

	/**
	 * 时间格式,相当于同时设置了exportFormat 和 importFormat
	 */
	public String format() default "";

	/**
	 * 导出时在excel中每个列的高度 单位为字符,一个汉字=2个字符
	 */
	public double height() default 10;

	/**
	 * 导出类型 1 从file读取_old ,2 是从数据库中读取字节文件, 3文件地址_new, 4网络地址 同样导入也是一样的
	 *
	 */
	public int imageType() default 3;

	/**
	 * 导入的时间格式,以这个是否为空来判断是否需要格式化日期
	 */
	public String importFormat() default "";

	/**
	 * 文字后缀,如% 90 变成90%
	 */
	public String suffix() default "";

	/**
	 * 是否换行 即支持\n
	 */
	public boolean isWrap() default true;

	/**
	 * 合并单元格依赖关系,比如第二列合并是基于第一列 则{1}就可以了
	 */
	public int[] mergeRely() default {};

	/**
	 * 纵向合并内容相同的单元格
	 */
	public boolean mergeVertical() default false;

	/**
	 * 导出时,对应数据库的字段 主要是用户区分每个字段, 不能有annocation重名的 导出时的列名
	 * 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用
	 */
	public String name();

	/**
	 * 是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row)
	 */
	public boolean needMerge() default false;

	/**
	 * 展示到第几个可以使用a_id,b_id来确定不同排序
	 */
	public String orderNum() default "0";

	/**
	 * 值得替换 导出是{"男_1","女_0"} 导入反过来,所以只用写一个
	 */
	public String[] replace() default {};
	
	/**
	 * 导入路径,如果是图片可以填写,默认是upload/className/ IconEntity这个类对应的就是upload/Icon/
	 *
	 */
	public String savePath() default "upload";

	/**
	 * 导出类型 0是常规, 1 是文本 2 是图片,3是函数,4是数字 默认是文本
	 */
	public int type() default 1;

	/**
	 * 导出时在excel中每个列的宽 单位为字符,一个汉字=2个字符 如 以列名列内容中较合适的长度 例如姓名列6 【姓名一般三个字】
	 * 性别列4【男女占1,但是列标题两个汉字】 限制1-255
	 */
	public double width() default 10;

	/**
	 * 是否自动统计数据,如果是统计,true的话在最后追加一行统计,把所有数据都和 这个处理会吞没异常,请注意这一点
	 * 
	 * @return
	 */
	public boolean isStatistics() default false;
	
	/**
	 * 方法描述: 数据字典表
	 * 作    者: yiming.zhang
	 * 日    期: 2014年5月11日-下午5:26:40
	 * @return 
	 * 返回类型: String
	 */
	public String dictTable() default "";

	/**
	 * 方法描述:  数据code
	 * 作    者: yiming.zhang
	 * 日    期: 2014年5月13日-下午9:37:16
	 * @return 
	 * 返回类型: String
	 */
	public String dicCode() default "";
	
	/**
	 * 方法描述:  数据Text
	 * 作    者: yiming.zhang
	 * 日    期: 2014年5月11日-下午5:29:05
	 * @return 
	 * 返回类型: String
	 */
	public String dicText() default "";
	
	/**
	 * 导入数据是否需要转化  
	 * 若是为true,则需要在pojo中加入 方法:convertset字段名(String text)  
	 * @return
	 */
	public boolean importConvert() default false;
	/**
	 * 导出数据是否需要转化
	 * 若是为true,则需要在pojo中加入方法:convertget字段名()
	 * @return
	 */
	public boolean exportConvert() default false;
	
	/**
	 * 值的替换是否支持替换多个(默认true,若数据库值本来就包含逗号则需要配置该值为false)
	 * @author taoYan
	 * @since 2018年8月1日
	 */
	public boolean multiReplace() default true;

	/**
	 * 父表头
	 * @return
	 */
	String groupName() default "";

	/**
	 * 数字格式化,参数是Pattern,使用的对象是DecimalFormat
	 * @return
	 */
	String numFormat() default "";
	/**
	 *  是否需要隐藏该列
	 * @return
	 */
	public boolean isColumnHidden() default  false;
	/**
	 * 固定的某一列,解决不好解析的问题
	 * @return
	 */
	public int fixedIndex() default -1;
	/**
	 * 这个是不是超链接,如果是需要实现接口返回对象
	 * @return
	 */
	public boolean isHyperlink() default false;
	
	//update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列
	/**
	 * 是否为动态列
	 */
	public boolean dynamic() default false;

	/**
	 * 动态列标题字段名,默认 name
	 */
	public String dynamicField() default "name";

	/**
	 * 动态列表格值字段名,默认 value
	 */
	public String dynamicVal() default "value";

	/**
	 * 是否保留原始字段列,默认不保留
	 */
	public boolean dynamicKeepSelf() default false;
	//update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列
}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/ExcelCollection.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;

/**
 * 导出的集合
 * 
 * @author JEECG 2013年8月24日
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelCollection {

	/**
	 * 定义excel导出ID 来限定导出字段,处理一个类对应多个不同名称的情况
	 */
	public String id() default "";

	/**
	 * 导出时,对应数据库的字段 主要是用户区分每个字段, 不能有annocation重名的 导出时的列名
	 * 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用
	 */
	public String name();

	/**
	 * 展示到第几个同样可以使用a_id,b_id
	 * 
	 */
	public String orderNum() default "0";

	/**
	 * 创建时创建的类型 默认值是 arrayList
	 */
	public Class type() default ArrayList.class;
}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/ExcelEntity.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 标记是不是导出excel 标记为实体类
 * 
 * @author JEECG
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelEntity {

	/**
	 * 定义excel导出ID 来限定导出字段,处理一个类对应多个不同名称的情况
	 */
	public String id() default "";

	/**
	 * 导出时,对应数据库的字段 主要是用户区分每个字段, 不能有annocation重名的 导出时的列名
	 * 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用
	 */
	public String name() default "";

	/**
	 * 导出时,是否展示name对应的文本
	 * @return
	 */
	boolean show() default false;
}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/ExcelIgnore.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 标记为excel 创建实体忽略,放置死循环的造成
 * 
 * @author JEECG
 * @date 2013-9-24
 * @version 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelIgnore {

}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/ExcelTarget.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * excel 导出是用于标记id的
 * 
 * @author JEECG
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface ExcelTarget {
	/**
	 * 定义excel导出ID 来限定导出字段
	 */
	public String value();
}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/ExcelVerify.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Excel 导入校验
 * 
 * @author JEECG
 * @date 2014年6月23日 下午10:46:26
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelVerify {
	/**
	 * 接口校验
	 * 
	 * @return
	 */
	public boolean interHandler() default false;

	/**
	 * 是电子邮件
	 * 
	 * @return
	 */
	public boolean isEmail() default false;

	/**
	 * 是13位移动电话
	 * 
	 * @return
	 */
	public boolean isMobile() default false;

	/**
	 * 是座机号码
	 * 
	 * @return
	 */
	public boolean isTel() default false;

	/**
	 * 最大长度
	 * 
	 * @return
	 */
	public int maxLength() default -1;

	/**
	 * 最小长度
	 * 
	 * @return
	 */
	public int minLength() default -1;

	/**
	 * 不允许空
	 * 
	 * @return
	 */
	public boolean notNull() default false;

	/**
	 * 正在表达式
	 * 
	 * @return
	 */
	public String regex() default "";

	/**
	 * 正在表达式,错误提示信息
	 * 
	 * @return
	 */
	public String regexTip() default "数据不符合规范";

}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/ExcelBaseParams.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity;

import org.jeecgframework.poi.handler.inter.IExcelDataHandler;

/**
 * 基础参数
 * 
 * @author JEECG
 * @date 2014年6月20日 下午1:56:52
 */
public class ExcelBaseParams {

	/**
	 * 数据处理接口,以此为主,replace,format都在这后面
	 */
	private IExcelDataHandler dataHanlder;

	public IExcelDataHandler getDataHanlder() {
		return dataHanlder;
	}

	public void setDataHanlder(IExcelDataHandler dataHanlder) {
		this.dataHanlder = dataHanlder;
	}

}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/ExportParams.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity;

import org.apache.poi.ss.usermodel.IndexedColors;
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
import org.jeecgframework.poi.excel.export.styler.ExcelExportStylerDefaultImpl;

/**
 * Excel 导出参数
 * 
 * @author JEECG
 * @version 1.0 2013年8月24日
 */
public class ExportParams extends ExcelBaseParams {

	/**
	 * 表格名称
	 */
	private String title;

	/**
	 * 表格名称
	 */
	private short titleHeight = 10;

	/**
	 * 第二行名称
	 */
	private String secondTitle;

	/**
	 * 表格名称
	 */
	private short secondTitleHeight = 8;
	/**
	 * sheetName
	 */
	private String sheetName;
	/**
	 * 过滤的属性
	 */
	private String[] exclusions;
	/**
	 * 是否添加需要需要
	 */
	private boolean addIndex;
	/**
	 * 是否添加需要需要
	 */
	private String indexName = "序号";
	/**
	 * 冰冻列
	 */
	private int freezeCol;
	/**
	 * 表头颜色
	 */
	private short color = IndexedColors.WHITE.index;
	/**
	 * 属性说明行的颜色 例如:HSSFColor.SKY_BLUE.index 默认
	 */
	private short headerColor = IndexedColors.SKY_BLUE.index;
	/**
	 * Excel 导出版本
	 */
	private ExcelType type = ExcelType.HSSF;
	/**
	 * Excel 导出style
	 */
	private Class style = ExcelExportStylerDefaultImpl.class;
	/**
	 * 是否创建表头
	 */
	private boolean isCreateHeadRows = true;

	/**
	 * 本地文件存储根路径  base path
	 */
	private String imageBasePath;
//update-begin---author:liusq  Date:20220104  for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】----
	/**
	 * 是否固定表头
	 */
	private boolean isFixedTitle     = true;
	/**
	 * 单sheet最大值
	 * 03版本默认6W行,07默认100W
	 */
	private int     maxNum           = 0;
	/**
	 * 导出时在excel中每个列的高度 单位为字符,一个汉字=2个字符
	 * 全局设置,优先使用
	 */
	private short height = 0;

	/**
	 * 只读
	 */
	private boolean readonly = false;
//update-end---author:liusq  Date:20220104  for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】----
	public ExportParams() {

	}

	public ExportParams(String title, String sheetName) {
		this.title = title;
		this.sheetName = sheetName;
	}

	public ExportParams(String title, String sheetName, ExcelType type) {
		this.title = title;
		this.sheetName = sheetName;
		this.type = type;
	}

	public ExportParams(String title, String secondTitle, String sheetName) {
		this.title = title;
		this.secondTitle = secondTitle;
		this.sheetName = sheetName;
	}
    
    public ExportParams(String title, String secondTitle, String sheetName, ExcelType type) {
        this.title = title;
        this.secondTitle = secondTitle;
        this.sheetName = sheetName;
        this.type = type;
    }

	public ExportParams(String title, String secondTitle, String sheetName,String imageBasePath) {
		this.title = title;
		this.secondTitle = secondTitle;
		this.sheetName = sheetName;
		this.imageBasePath = imageBasePath;
	}

	public short getColor() {
		return color;
	}

	public String[] getExclusions() {
		return exclusions;
	}

	public short getHeaderColor() {
		return headerColor;
	}

	public String getSecondTitle() {
		return secondTitle;
	}

	public short getSecondTitleHeight() {
		return (short) (secondTitleHeight * 50);
	}

	public String getSheetName() {
		return sheetName;
	}

	public String getTitle() {
		return title;
	}

	public short getTitleHeight() {
		return (short) (titleHeight * 50);
	}

	public boolean isAddIndex() {
		return addIndex;
	}

	public void setAddIndex(boolean addIndex) {
		this.addIndex = addIndex;
	}

	public void setColor(short color) {
		this.color = color;
	}

	public void setExclusions(String[] exclusions) {
		this.exclusions = exclusions;
	}

	public void setHeaderColor(short headerColor) {
		this.headerColor = headerColor;
	}

	public void setSecondTitle(String secondTitle) {
		this.secondTitle = secondTitle;
	}

	public void setSecondTitleHeight(short secondTitleHeight) {
		this.secondTitleHeight = secondTitleHeight;
	}

	public void setSheetName(String sheetName) {
		this.sheetName = sheetName;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public void setTitleHeight(short titleHeight) {
		this.titleHeight = titleHeight;
	}

	public ExcelType getType() {
		return type;
	}

	public void setType(ExcelType type) {
		this.type = type;
	}

	public String getIndexName() {
		return indexName;
	}

	public void setIndexName(String indexName) {
		this.indexName = indexName;
	}

	public Class getStyle() {
		return style;
	}

	public void setStyle(Class style) {
		this.style = style;
	}

	public int getFreezeCol() {
		return freezeCol;
	}

	public void setFreezeCol(int freezeCol) {
		this.freezeCol = freezeCol;
	}

	public boolean isCreateHeadRows() {
		return isCreateHeadRows;
	}

	public void setCreateHeadRows(boolean isCreateHeadRows) {
		this.isCreateHeadRows = isCreateHeadRows;
	}

	public String getImageBasePath() {
		return imageBasePath;
	}

	public void setImageBasePath(String imageBasePath) {
		this.imageBasePath = imageBasePath;
	}

	public int getMaxNum() {
		return maxNum;
	}

	public void setMaxNum(int maxNum) {
		this.maxNum = maxNum;
	}

	public short getHeight() {
		return height == -1 ? -1 : (short) (height * 50);
	}

	public void setHeight(short height) {
		this.height = height;
	}

	public boolean isFixedTitle() {
		return isFixedTitle;
	}

	public void setFixedTitle(boolean fixedTitle) {
		isFixedTitle = fixedTitle;
	}

	public boolean isReadonly() {
		return readonly;
	}

	public void setReadonly(boolean readonly) {
		this.readonly = readonly;
	}
}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/ImportParams.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity;

import org.jeecgframework.poi.handler.inter.IExcelVerifyHandler;

import java.util.List;

/**
 * 导入参数设置
 * 
 * @author JEECG
 * @date 2013-9-24
 * @version 1.0
 */
public class ImportParams extends ExcelBaseParams {
	/**
	 * 表格标题行数,默认0
	 */
	private int titleRows = 0;
	/**
	 * 表头行数,默认1
	 */
	private int headRows = 1;
	/**
	 * 字段真正值和列标题之间的距离 默认0
	 */
	private int startRows = 0;
	/**
	 * 主键设置,如何这个cell没有值,就跳过 或者认为这个是list的下面的值
	 */
	private int keyIndex = 0;
	//update-begin-author:liusq date:20220605 for:https://gitee.com/jeecg/jeecg-boot/issues/I57UPC 导入 ImportParams 中没有startSheetIndex参数
	/**
	 * 开始读取的sheet位置,默认为0
	 */
	private int                 startSheetIndex  = 0;
	//update-end-author:liusq date:20220605 for:https://gitee.com/jeecg/jeecg-boot/issues/I57UPC 导入 ImportParams 中没有startSheetIndex参数

	//update-begin-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错
	/**
	 * 上传表格需要读取的sheet 数量,默认为0
	 */
	private int sheetNum = 0;
	//update-end-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错
	/**
	 * 是否需要保存上传的Excel,默认为false
	 */
	private boolean needSave = false;
	/**
	 * 保存上传的Excel目录,默认是 如 TestEntity这个类保存路径就是
	 * upload/excelUpload/Test/yyyyMMddHHmss_***** 保存名称上传时间_五位随机数
	 */
	private String saveUrl = "upload/excelUpload";
	/**
	 * 校验处理接口
	 */
	private IExcelVerifyHandler verifyHanlder;
	/**
	 * 最后的无效行数
	 */
	private int lastOfInvalidRow = 0;

	/**
	 * 不需要解析的表头 只作为多表头展示,无字段与其绑定
	 */
	private List ignoreHeaderList;

	/**
	 * 指定导入的sheetName
	 */
	private String sheetName;

	/**
	 * 图片列 集合
	 */
	private List imageList;

	public int getHeadRows() {
		return headRows;
	}

	public int getKeyIndex() {
		return keyIndex;
	}

	public String getSaveUrl() {
		return saveUrl;
	}

	public int getSheetNum() {
		return sheetNum;
	}

	public int getStartRows() {
		return startRows;
	}

	public int getTitleRows() {
		return titleRows;
	}

	public IExcelVerifyHandler getVerifyHanlder() {
		return verifyHanlder;
	}

	public boolean isNeedSave() {
		return needSave;
	}

	public void setHeadRows(int headRows) {
		this.headRows = headRows;
	}

	public void setKeyIndex(int keyIndex) {
		this.keyIndex = keyIndex;
	}

	public void setNeedSave(boolean needSave) {
		this.needSave = needSave;
	}

	public void setSaveUrl(String saveUrl) {
		this.saveUrl = saveUrl;
	}

	public void setSheetNum(int sheetNum) {
		this.sheetNum = sheetNum;
	}

	public void setStartRows(int startRows) {
		this.startRows = startRows;
	}

	public void setTitleRows(int titleRows) {
		this.titleRows = titleRows;
	}

	public void setVerifyHanlder(IExcelVerifyHandler verifyHanlder) {
		this.verifyHanlder = verifyHanlder;
	}

	public int getLastOfInvalidRow() {
		return lastOfInvalidRow;
	}

	public void setLastOfInvalidRow(int lastOfInvalidRow) {
		this.lastOfInvalidRow = lastOfInvalidRow;
	}

	public List getImageList() {
		return imageList;
	}

	public void setImageList(List imageList) {
		this.imageList = imageList;
	}

	public List getIgnoreHeaderList() {
		return ignoreHeaderList;
	}

	public void setIgnoreHeaderList(List ignoreHeaderList) {
		this.ignoreHeaderList = ignoreHeaderList;
	}

	public String getSheetName() {
		return sheetName;
	}

	public void setSheetName(String sheetName) {
		this.sheetName = sheetName;
	}

	/**
	 * 根据表头显示的文字 判断是否忽略该表头
	 * @param text
	 * @return
	 */
	public boolean isIgnoreHeader(String text){
		if(ignoreHeaderList!=null && ignoreHeaderList.indexOf(text)>=0){
			return true;
		}
		return false;
	}
	public int getStartSheetIndex() {
		return startSheetIndex;
	}

	public void setStartSheetIndex(int startSheetIndex) {
		this.startSheetIndex = startSheetIndex;
	}
}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/TemplateExportParams.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity;

import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.jeecgframework.poi.excel.export.styler.ExcelExportStylerDefaultImpl;

import java.io.IOException;
import java.io.InputStream;

/**
 * 模板导出参数设置
 * 
 * @author JEECG
 * @date 2013-10-17
 * @version 1.0
 */
public class TemplateExportParams extends ExcelBaseParams {

	/**
	 * 输出全部的sheet
	 */
	private boolean scanAllsheet = false;
	/**
	 * 模板的路径
	 */
	private String templateUrl;
	/**
	 * 模板
	 */
	private Workbook templateWb;

	/**
	 * 需要导出的第几个 sheetNum,默认是第0个
	 */
	private Integer[] sheetNum = new Integer[] { 0 };

	/**
	 * 这只sheetName 不填就使用原来的
	 */
	private String[] sheetName;

	/**
	 * 表格列标题行数,默认1
	 */
	private int headingRows = 1;

	/**
	 * 表格列标题开始行,默认1
	 */
	private int headingStartRow = 1;
	/**
	 * 设置数据源的NUM
	 */
	private int dataSheetNum = 0;
	/**
	 * Excel 导出style
	 */
	private Class style = ExcelExportStylerDefaultImpl.class;
	/**
	 * FOR EACH 用到的局部变量
	 */
	private String tempParams = "t";
    //列循环支持
	private boolean   colForEach      = false;

	/**
	 * 默认构造器
	 */
	public TemplateExportParams() {

	}

	/**
	 * 构造器
	 * 
	 * @param templateUrl
	 *            模板路径
	 * @param scanAllsheet
	 *            是否输出全部的sheet
	 * @param sheetName
	 *            sheet的名称,可不填
	 */
	public TemplateExportParams(String templateUrl, boolean scanAllsheet, String... sheetName) {
		this.templateUrl = templateUrl;
		this.scanAllsheet = scanAllsheet;
		if (sheetName != null && sheetName.length > 0) {
			this.sheetName = sheetName;

		}
	}

	/**
	 * 构造器
	 * 
	 * @param templateUrl
	 *            模板路径
	 * @param sheetNum
	 *            sheet 的位置,可不填
	 */
	public TemplateExportParams(String templateUrl, Integer... sheetNum) {
		this.templateUrl = templateUrl;
		if (sheetNum != null && sheetNum.length > 0) {
			this.sheetNum = sheetNum;
		}
	}

	/**
	 * 单个sheet输出构造器
	 * 
	 * @param templateUrl
	 *            模板路径
	 * @param sheetName
	 *            sheet的名称
	 * @param sheetNum
	 *            sheet的位置,可不填
	 */
	public TemplateExportParams(String templateUrl, String sheetName, Integer... sheetNum) {
		this.templateUrl = templateUrl;
		this.sheetName = new String[] { sheetName };
		if (sheetNum != null && sheetNum.length > 0) {
			this.sheetNum = sheetNum;
		}
	}
	//update-begin-author:liusq---date:2024-09-03--for: [issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式
	/**
	 * 构造器
	 * @param inputStream 输入流
	 * @param scanAllsheet 是否输出全部的sheet
	 * @param sheetName    sheet的名称,可不填
	 */
	public TemplateExportParams(InputStream inputStream, boolean scanAllsheet, String... sheetName) throws IOException {
		this.templateWb = WorkbookFactory.create(inputStream);
		this.scanAllsheet = scanAllsheet;
		if (sheetName != null && sheetName.length > 0) {
			this.sheetName = sheetName;
		}
	}
	/**
	 * 构造器
	 * @param inputStream 输入流
	 * @param sheetNum    sheet 的位置,可不填
	 */
	public TemplateExportParams(InputStream inputStream, Integer... sheetNum) throws IOException {
		this.templateWb = WorkbookFactory.create(inputStream);
		if (sheetNum != null && sheetNum.length > 0) {
			this.sheetNum = sheetNum;
		}
	}

	/**
	 * 单个sheet输出构造器
	 * @param inputStream 输入流
	 * @param sheetName   sheet的名称
	 * @param sheetNum    sheet的位置,可不填
	 */
	public TemplateExportParams(InputStream inputStream, String sheetName, Integer... sheetNum) throws IOException {
		this.templateWb = WorkbookFactory.create(inputStream);
		this.sheetName = new String[] { sheetName };
		if (sheetNum != null && sheetNum.length > 0) {
			this.sheetNum = sheetNum;
		}
	}
	//update-end-author:liusq---date:2024-09-03--for: [issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式
	public int getHeadingRows() {
		return headingRows;
	}

	public int getHeadingStartRow() {
		return headingStartRow;
	}

	public String[] getSheetName() {
		return sheetName;
	}

	public Integer[] getSheetNum() {
		return sheetNum;
	}

	public String getTemplateUrl() {
		return templateUrl;
	}

	public void setHeadingRows(int headingRows) {
		this.headingRows = headingRows;
	}

	public void setHeadingStartRow(int headingStartRow) {
		this.headingStartRow = headingStartRow;
	}

	public void setSheetName(String[] sheetName) {
		this.sheetName = sheetName;
	}

	public void setSheetName(String sheetName) {
		this.sheetName = new String[] { sheetName };
	}

	public void setSheetNum(Integer[] sheetNum) {
		this.sheetNum = sheetNum;
	}

	public void setSheetNum(Integer sheetNum) {
		this.sheetNum = new Integer[] { sheetNum };
	}

	public void setTemplateUrl(String templateUrl) {
		this.templateUrl = templateUrl;
	}

	public Class getStyle() {
		return style;
	}

	public void setStyle(Class style) {
		this.style = style;
	}

	public int getDataSheetNum() {
		return dataSheetNum;
	}

	public void setDataSheetNum(int dataSheetNum) {
		this.dataSheetNum = dataSheetNum;
	}

	public boolean isScanAllsheet() {
		return scanAllsheet;
	}

	public void setScanAllsheet(boolean scanAllsheet) {
		this.scanAllsheet = scanAllsheet;
	}

	public String getTempParams() {
		return tempParams;
	}

	public void setTempParams(String tempParams) {
		this.tempParams = tempParams;
	}

	public boolean isColForEach() {
		return colForEach;
	}

	public void setColForEach(boolean colForEach) {
		this.colForEach = colForEach;
	}
	public Workbook getTemplateWb() {
		return templateWb;
	}

	public void setTemplateWb(Workbook templateWb) {
		this.templateWb = templateWb;
	}
}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/enmus/CellValueType.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.enmus;

/**
 * Cell 值得类型
 * 
 * @author JEECG
 * @date 2014年12月29日 下午10:20:49
 */
public enum CellValueType {

	String, Number, Boolean, Date, TElement, Null, None;

}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/enmus/ExcelStyleType.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.enmus;

import org.jeecgframework.poi.excel.export.styler.ExcelExportStylerBorderImpl;
import org.jeecgframework.poi.excel.export.styler.ExcelExportStylerColorImpl;
import org.jeecgframework.poi.excel.export.styler.ExcelExportStylerDefaultImpl;

/**
 * 插件提供的几个默认样式
 * 
 * @author JEECG
 * @date 2015年1月9日 下午9:02:24
 */
public enum ExcelStyleType {

	NONE("默认样式", ExcelExportStylerDefaultImpl.class), BORDER("边框样式", ExcelExportStylerBorderImpl.class), COLOR("间隔行样式", ExcelExportStylerColorImpl.class);

	private String name;
	private Class clazz;

	ExcelStyleType(String name, Class clazz) {
		this.name = name;
		this.clazz = clazz;
	}

	public Class getClazz() {
		return clazz;
	}

	public String getName() {
		return name;
	}

}


================================================
FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/enmus/ExcelType.java
================================================
/**
 * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.enmus;

/**
 * Excel 文件格式类型枚举
 * 

用于指定导出/导入的 Excel 文件格式版本

* * @author JEECG * @date 2014年12月29日 下午9:08:21 */ public enum ExcelType { /** * HSSF 格式 - Excel 97-2003 版本 (.xls) *
    *
  • 文件扩展名:.xls
  • *
  • 最大行数:65,536 行(2^16)
  • *
  • 最大列数:256 列(2^8)
  • *
  • 适用场景:兼容老版本 Excel,数据量较小的场景
  • *
  • 对应 POI 类:HSSFWorkbook
  • *
*/ HSSF, /** * XSSF 格式 - Excel 2007+ 版本 (.xlsx) *
    *
  • 文件扩展名:.xlsx
  • *
  • 最大行数:1,048,576 行(2^20)
  • *
  • 最大列数:16,384 列(2^14)
  • *
  • 适用场景:现代 Excel 版本,大数据量导出,推荐使用
  • *
  • 对应 POI 类:XSSFWorkbook
  • *
  • 优势:支持更大数据量,文件压缩比更高,功能更丰富
  • *
*

注意:导出时请确保文件扩展名与格式类型匹配,避免文件损坏

*/ XSSF; } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelBaseEntity.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.params; import java.lang.reflect.Method; import java.util.List; /** * Excel 导入导出基础对象类 * * @author JEECG * @date 2014年6月20日 下午2:26:09 */ public class ExcelBaseEntity { /** * 对应name */ protected String name; /** * 对应type */ private int type = 1; /** * 数据库格式 */ private String databaseFormat; /** * 导出日期格式 */ private String format; /** * 数字格式化,参数是Pattern,使用的对象是DecimalFormat */ private String numFormat; /** * 替换值表达式 :"男_1","女_0" */ private String[] replace; /** * 替换是否是替换多个值 */ private boolean multiReplace; /** * 表头组名称 */ private String groupName; /** * set/get方法 */ private Method method; /** * 固定的列 */ private Integer fixedIndex; /** * 字典名称 */ private String dict; /** * 这个是不是超链接,如果是需要实现接口返回对象 */ private boolean hyperlink; private List methods; public String getDatabaseFormat() { return databaseFormat; } public String getFormat() { return format; } public Method getMethod() { return method; } public List getMethods() { return methods; } public String getName() { return name; } public String[] getReplace() { return replace; } public int getType() { return type; } public void setDatabaseFormat(String databaseFormat) { this.databaseFormat = databaseFormat; } public void setFormat(String format) { this.format = format; } public void setMethod(Method method) { this.method = method; } public void setMethods(List methods) { this.methods = methods; } public void setName(String name) { this.name = name; } public void setReplace(String[] replace) { this.replace = replace; } public void setType(int type) { this.type = type; } public boolean isMultiReplace() { return multiReplace; } public void setMultiReplace(boolean multiReplace) { this.multiReplace = multiReplace; } public String getNumFormat() { return numFormat; } public void setNumFormat(String numFormat) { this.numFormat = numFormat; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public Integer getFixedIndex() { return fixedIndex; } public void setFixedIndex(Integer fixedIndex) { this.fixedIndex = fixedIndex; } public String getDict() { return dict; } public void setDict(String dict) { this.dict = dict; } public boolean isHyperlink() { return hyperlink; } public void setHyperlink(boolean hyperlink) { this.hyperlink = hyperlink; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelCollectionParams.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.params; import java.util.Map; /** * Excel 对于的 Collection * * @author JEECG * @date 2013-9-26 * @version 1.0 */ public class ExcelCollectionParams { /** * 集合对应的名称 */ private String name; /** * Excel 列名称 */ private String excelName; /** * 实体对象 */ private Class type; /** * 这个list下面的参数集合实体对象 */ private Map excelParams; public Map getExcelParams() { return excelParams; } public String getName() { return name; } public Class getType() { return type; } public void setExcelParams(Map excelParams) { this.excelParams = excelParams; } public void setName(String name) { this.name = name; } public void setType(Class type) { this.type = type; } public String getExcelName() { return excelName; } public void setExcelName(String excelName) { this.excelName = excelName; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelExportEntity.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.params; import java.util.ArrayList; import java.util.List; /** * excel 导出工具类,对cell类型做映射 * * @author JEECG * @version 1.0 2013年8月24日 */ public class ExcelExportEntity extends ExcelBaseEntity implements Comparable { /** * 如果是MAP导出,这个是map的key */ private Object key; private double width = 10; private double height = 10; /** * 图片的类型,1是文件地址(class目录),2是数据库字节,3是文件地址(磁盘目录),4网络图片 */ private int exportImageType = 3; /** * 图片储存位置(磁盘目录) 用于导出获取图片绝对路径 */ private String imageBasePath; /** * 排序顺序 */ private int orderNum = 0; /** * 是否支持换行 */ private boolean isWrap; /** * 是否需要合并 */ private boolean needMerge; /** * 单元格纵向合并 */ private boolean mergeVertical; /** * 合并依赖 */ private int[] mergeRely; /** * 后缀 */ private String suffix; /** * 统计 */ private boolean isStatistics; /** * 是否横向合并 */ private boolean colspan; /** * 被横向合并的列名称 */ private List subColumnList; /** * 父表头的名称 */ private String groupName; /** * 是否隐藏列 */ private boolean isColumnHidden; //update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 /** * 是否为动态列 */ private boolean dynamic; /** * 动态列标题字段 */ private String dynamicField; /** * 动态列值字段 */ private String dynamicValue; /** * 是否保留原始列 */ private boolean dynamicKeepSelf; /** * 当前动态列对应的标题名称 */ private String dynamicColumnName; //update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 private List list; public ExcelExportEntity() { } public ExcelExportEntity(String name) { super.name = name; } public ExcelExportEntity(String name, Object key) { super.name = name; this.key = key; } /** * 构造器 * @param name 描述-文字 * @param key 存储key 如果是MAP导出,这个是map的key * @param colspan 是否为合并列(a,b列公用一个表头c,则a,b,c都需要设置为true) */ public ExcelExportEntity(String name, Object key, boolean colspan) { super.name = name; this.key = key; this.colspan = colspan; this.needMerge = colspan; } public ExcelExportEntity(String name, Object key, int width) { super.name = name; this.width = width; this.key = key; } public int getExportImageType() { return exportImageType; } public double getHeight() { return height; } public Object getKey() { return key; } public List getList() { return list; } public int[] getMergeRely() { return mergeRely == null ? new int[0] : mergeRely; } public int getOrderNum() { return orderNum; } public double getWidth() { return width; } public boolean isMergeVertical() { return mergeVertical; } public boolean isNeedMerge() { return needMerge; } public boolean isWrap() { return isWrap; } public void setExportImageType(int exportImageType) { this.exportImageType = exportImageType; } public void setHeight(double height) { this.height = height; } public void setKey(Object key) { this.key = key; } public void setList(List list) { this.list = list; } public void setMergeRely(int[] mergeRely) { this.mergeRely = mergeRely; } public void setMergeVertical(boolean mergeVertical) { this.mergeVertical = mergeVertical; } public void setNeedMerge(boolean needMerge) { this.needMerge = needMerge; } public void setOrderNum(int orderNum) { this.orderNum = orderNum; } public void setWidth(double width) { this.width = width; } public void setWrap(boolean isWrap) { this.isWrap = isWrap; } public String getSuffix() { return suffix; } public void setSuffix(String suffix) { this.suffix = suffix; } public boolean isStatistics() { return isStatistics; } public void setStatistics(boolean isStatistics) { this.isStatistics = isStatistics; } public String getImageBasePath() { return imageBasePath; } public void setImageBasePath(String imageBasePath) { this.imageBasePath = imageBasePath; } public boolean isColspan() { return colspan; } public void setColspan(boolean colspan) { this.colspan = colspan; } public List getSubColumnList() { return subColumnList; } public void setSubColumnList(List subColumnList) { this.subColumnList = subColumnList; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public boolean isColumnHidden() { return isColumnHidden; } public void setColumnHidden(boolean columnHidden) { isColumnHidden = columnHidden; } /** * 是否为合并子列 * @return */ public boolean isSubColumn(){ return this.colspan && (this.subColumnList==null || this.subColumnList.size()==0); } /** * 是否为合并父列 * @return */ public boolean isMergeColumn(){ return this.colspan && this.subColumnList!=null && this.subColumnList.size()>0; } public boolean isDynamic() { return dynamic; } public void setDynamic(boolean dynamic) { this.dynamic = dynamic; } public String getDynamicField() { return dynamicField; } public void setDynamicField(String dynamicField) { this.dynamicField = dynamicField; } public String getDynamicValue() { return dynamicValue; } public void setDynamicValue(String dynamicValue) { this.dynamicValue = dynamicValue; } public boolean isDynamicKeepSelf() { return dynamicKeepSelf; } public void setDynamicKeepSelf(boolean dynamicKeepSelf) { this.dynamicKeepSelf = dynamicKeepSelf; } public String getDynamicColumnName() { return dynamicColumnName; } public void setDynamicColumnName(String dynamicColumnName) { this.dynamicColumnName = dynamicColumnName; } /** * 获取被合并的子列 * @param all * @return */ public List initSubExportEntity(List all){ List sub = new ArrayList(); for (ExcelExportEntity temp : all) { if(this.subColumnList.contains(temp.getKey())){ sub.add(temp); } } this.setList(sub); return sub; } @Override public int compareTo(ExcelExportEntity prev) { return this.getOrderNum() - prev.getOrderNum(); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelForEachParams.java ================================================ package org.jeecgframework.poi.excel.entity.params; import java.io.Serializable; import java.util.Stack; import org.apache.poi.ss.usermodel.CellStyle; /** * 模板for each是的参数 * @author JueYue * @date 2015年4月29日 下午9:22:48 */ public class ExcelForEachParams implements Serializable { /** * */ private static final long serialVersionUID = 1L; /** * key */ private String name; /** * key */ private Stack tempName; /** * 模板的cellStyle */ private CellStyle cellStyle; /** * 行高 */ private short height; /** * 常量值 */ private String constValue; /** * 列合并 */ private int colspan = 1; /** * 行合并 */ private int rowspan = 1; /** * 行合并 */ private boolean collectCell; public ExcelForEachParams() { } public ExcelForEachParams(String name, CellStyle cellStyle, short height) { this.name = name; this.cellStyle = cellStyle; this.height = height; } public String getName() { return name; } public void setName(String name) { this.name = name; } public CellStyle getCellStyle() { return cellStyle; } public void setCellStyle(CellStyle cellStyle) { this.cellStyle = cellStyle; } public short getHeight() { return height; } public void setHeight(short height) { this.height = height; } public String getConstValue() { return constValue; } public void setConstValue(String constValue) { this.constValue = constValue; } public int getColspan() { return colspan; } public void setColspan(int colspan) { this.colspan = colspan; } public int getRowspan() { return rowspan; } public void setRowspan(int rowspan) { this.rowspan = rowspan; } public boolean isCollectCell() { return collectCell; } public void setCollectCell(boolean collectCell) { this.collectCell = collectCell; } public Stack getTempName() { return tempName; } public void setTempName(Stack tempName) { this.tempName = tempName; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelImportEntity.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.params; import java.util.List; /** * excel 导入工具类,对cell类型做映射 * * @author JEECG * @version 1.0 2013年8月24日 */ public class ExcelImportEntity extends ExcelBaseEntity { /** * 对应 Collection NAME */ private String collectionName; /** * 保存图片的地址 当saveType设置为3/4时,此值可以设置为:local,minio,alioss */ private String saveUrl; /** * 保存图片的类型,1是文件_old,2是数据库字节,3文件地址_new,4网络地址 */ private int saveType; /** * 对应exportType */ private String classType; /** * 校驗參數 */ private ExcelVerifyEntity verify; /** * 后缀 */ private String suffix; private List list; public String getClassType() { return classType; } public String getCollectionName() { return collectionName; } public List getList() { return list; } public int getSaveType() { return saveType; } public String getSaveUrl() { return saveUrl; } public ExcelVerifyEntity getVerify() { return verify; } public void setClassType(String classType) { this.classType = classType; } public void setCollectionName(String collectionName) { this.collectionName = collectionName; } public void setList(List list) { this.list = list; } public void setSaveType(int saveType) { this.saveType = saveType; } public void setSaveUrl(String saveUrl) { this.saveUrl = saveUrl; } public void setVerify(ExcelVerifyEntity verify) { this.verify = verify; } public String getSuffix() { return suffix; } public void setSuffix(String suffix) { this.suffix = suffix; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelTemplateParams.java ================================================ package org.jeecgframework.poi.excel.entity.params; import java.io.Serializable; import org.apache.poi.ss.usermodel.CellStyle; /** * 模板便利是的参数 * * @author JEECG * @date 2015年4月29日 下午9:22:48 */ public class ExcelTemplateParams implements Serializable { /** * */ private static final long serialVersionUID = 1L; /** * key */ private String name; /** * 模板的cellStyle */ private CellStyle cellStyle; /** * 行高 */ private short height; public ExcelTemplateParams() { } public ExcelTemplateParams(String name, CellStyle cellStyle, short height) { this.name = name; this.cellStyle = cellStyle; this.height = height; } public String getName() { return name; } public void setName(String name) { this.name = name; } public CellStyle getCellStyle() { return cellStyle; } public void setCellStyle(CellStyle cellStyle) { this.cellStyle = cellStyle; } public short getHeight() { return height; } public void setHeight(short height) { this.height = height; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelVerifyEntity.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.params; /** * Excel 校验对象 * * @author JEECG * @date 2014年6月29日 下午4:24:59 */ public class ExcelVerifyEntity { /** * 接口校验 * * @return */ private boolean interHandler; /** * 不允许空 * * @return */ private boolean notNull; /** * 是13位移动电话 * * @return */ private boolean isMobile; /** * 是座机号码 * * @return */ private boolean isTel; /** * 是电子邮件 * * @return */ private boolean isEmail; /** * 最小长度 * * @return */ private int minLength; /** * 最大长度 * * @return */ private int maxLength; /** * 正在表达式 * * @return */ private String regex; /** * 正在表达式,错误提示信息 * * @return */ private String regexTip; public int getMaxLength() { return maxLength; } public int getMinLength() { return minLength; } public String getRegex() { return regex; } public String getRegexTip() { return regexTip; } public boolean isEmail() { return isEmail; } public boolean isInterHandler() { return interHandler; } public boolean isMobile() { return isMobile; } public boolean isNotNull() { return notNull; } public boolean isTel() { return isTel; } public void setEmail(boolean isEmail) { this.isEmail = isEmail; } public void setInterHandler(boolean interHandler) { this.interHandler = interHandler; } public void setMaxLength(int maxLength) { this.maxLength = maxLength; } public void setMinLength(int minLength) { this.minLength = minLength; } public void setMobile(boolean isMobile) { this.isMobile = isMobile; } public void setNotNull(boolean notNull) { this.notNull = notNull; } public void setRegex(String regex) { this.regex = regex; } public void setRegexTip(String regexTip) { this.regexTip = regexTip; } public void setTel(boolean isTel) { this.isTel = isTel; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/MergeEntity.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.params; import java.util.List; /** * 合并单元格使用对象 * * Created by jue on 14-6-11. */ public class MergeEntity { /** * 合并开始行 */ private int startRow; /** * 合并结束行 */ private int endRow; /** * 文字 */ private String text; /** * 依赖关系文本 */ private List relyList; public MergeEntity() { } public MergeEntity(String text, int startRow, int endRow) { this.text = text; this.endRow = endRow; this.startRow = startRow; } public int getEndRow() { return endRow; } public List getRelyList() { return relyList; } public int getStartRow() { return startRow; } public String getText() { return text; } public void setEndRow(int endRow) { this.endRow = endRow; } public void setRelyList(List relyList) { this.relyList = relyList; } public void setStartRow(int startRow) { this.startRow = startRow; } public void setText(String text) { this.text = text; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/result/ExcelImportResult.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.result; import java.util.List; import org.apache.poi.ss.usermodel.Workbook; /** * 导入返回类 * * @author JEECG * @date 2014年6月29日 下午5:12:10 */ public class ExcelImportResult { /** * 结果集 */ private List list; /** * 是否存在校验失败 */ private boolean verfiyFail; /** * 数据源 */ private Workbook workbook; public ExcelImportResult() { } public ExcelImportResult(List list, boolean verfiyFail, Workbook workbook) { this.list = list; this.verfiyFail = verfiyFail; this.workbook = workbook; } public List getList() { return list; } public Workbook getWorkbook() { return workbook; } public boolean isVerfiyFail() { return verfiyFail; } public void setList(List list) { this.list = list; } public void setVerfiyFail(boolean verfiyFail) { this.verfiyFail = verfiyFail; } public void setWorkbook(Workbook workbook) { this.workbook = workbook; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/result/ExcelVerifyHanlderResult.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.result; /** * Excel导入处理返回结果 * * @author JEECG * @date 2014年6月23日 下午11:03:29 */ public class ExcelVerifyHanlderResult { /** * 是否正确 */ private boolean success; /** * 错误信息 */ private String msg; public ExcelVerifyHanlderResult() { } public ExcelVerifyHanlderResult(boolean success) { this.success = success; } public ExcelVerifyHanlderResult(boolean success, String msg) { this.success = success; this.msg = msg; } public String getMsg() { return msg; } public boolean isSuccess() { return success; } public void setMsg(String msg) { this.msg = msg; } public void setSuccess(boolean success) { this.success = success; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/sax/SaxReadCellEntity.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.sax; import org.jeecgframework.poi.excel.entity.enmus.CellValueType; /** * Cell 对象 * * @author JEECG * @date 2014年12月29日 下午10:12:57 */ public class SaxReadCellEntity { /** * 值类型 */ private CellValueType cellType; /** * 值 */ private Object value; public SaxReadCellEntity(CellValueType cellType, Object value) { this.cellType = cellType; this.value = value; } public CellValueType getCellType() { return cellType; } public void setCellType(CellValueType cellType) { this.cellType = cellType; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } @Override public String toString() { return "[type=" + cellType.toString() + ",value=" + value + "]"; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/entity/vo/PoiBaseConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.entity.vo; /** * 基础常量 Created by jue on 14-4-21. */ public interface PoiBaseConstants { /** * 字段属性对应方法 */ public static String GET = "get"; /** * 字段属性对应方法 */ public static String SET = "set"; /** * 字段属性对应方法 */ public static String IS = "is"; /** * 是否增加属性列 */ public static String IS_ADD_INDEX = "isAddIndex"; /** * 字段属性对应convert方法 */ public static String CONVERT = "convert"; } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/export/ExcelBatchExportServer.java ================================================ package org.jeecgframework.poi.excel.export; import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.jeecgframework.poi.excel.annotation.ExcelTarget; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.entity.enmus.ExcelType; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.jeecgframework.poi.excel.entity.vo.PoiBaseConstants; import org.jeecgframework.poi.excel.export.styler.IExcelExportStyler; import org.jeecgframework.poi.exception.excel.ExcelExportException; import org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum; import org.jeecgframework.poi.handler.inter.IExcelExportServer; import org.jeecgframework.poi.handler.inter.IExcelExportServerEnhanced; import org.jeecgframework.poi.handler.inter.IWriter; import org.jeecgframework.poi.util.PoiExcelGraphDataUtil; import org.jeecgframework.poi.util.PoiPublicUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.util.*; import static org.jeecgframework.poi.excel.ExcelExportUtil.USE_SXSSF_LIMIT; /** * 提供批次插入服务 * @author liusq * @date 2022年1月4日 */ public class ExcelBatchExportServer extends ExcelExportServer implements IWriter { private final static Logger LOGGER = LoggerFactory.getLogger(ExcelBatchExportServer.class); private Workbook workbook; private Sheet sheet; private List excelParams; private ExportParams entity; private int titleHeight; private Drawing patriarch; private short rowHeight; private int index; public void init(ExportParams entity, Class pojoClass) { List excelParams = createExcelExportEntityList(entity, pojoClass); init(entity, excelParams); } /** * 初始化数据 * @param entity 导出参数 * @param excelParams */ public void init(ExportParams entity, List excelParams) { LOGGER.debug("ExcelBatchExportServer only support SXSSFWorkbook"); entity.setType(ExcelType.XSSF); workbook = new SXSSFWorkbook(); this.entity = entity; this.excelParams = excelParams; super.type = entity.getType(); createSheet(workbook, entity, excelParams); if (entity.getMaxNum() == 0) { entity.setMaxNum(USE_SXSSF_LIMIT); } insertDataToSheet(workbook, entity, excelParams, null, sheet); } public List createExcelExportEntityList(ExportParams entity, Class pojoClass) { try { List excelParams = new ArrayList(); if (entity.isAddIndex()) { excelParams.add(indexExcelEntity(entity)); } // 得到所有字段 Field[] fileds = PoiPublicUtil.getClassFields(pojoClass); ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class); String targetId = etarget == null ? null : etarget.value(); getAllExcelField(entity.getExclusions(), targetId, fileds, excelParams, pojoClass, null); sortAllParams(excelParams); return excelParams; } catch (Exception e) { throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e); } } public void createSheet(Workbook workbook, ExportParams entity, List excelParams) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Excel export start ,List is {}", excelParams); LOGGER.debug("Excel version is {}", entity.getType().equals(ExcelType.HSSF) ? "03" : "07"); } if (workbook == null || entity == null || excelParams == null) { throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR); } try { try { sheet = workbook.createSheet(entity.getSheetName()); } catch (Exception e) { // 重复遍历,出现了重名现象,创建非指定的名称Sheet sheet = workbook.createSheet(); } } catch (Exception e) { throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e); } } @Override protected void insertDataToSheet(Workbook workbook, ExportParams entity, List entityList, Collection> dataSet, Sheet sheet) { try { dataHanlder = entity.getDataHanlder(); if (dataHanlder != null && dataHanlder.getNeedHandlerFields() != null) { needHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields()); } // 创建表格样式 setExcelExportStyler((IExcelExportStyler) entity.getStyle() .getConstructor(Workbook.class).newInstance(workbook)); patriarch = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet); List excelParams = new ArrayList(); if (entity.isAddIndex()) { excelParams.add(indexExcelEntity(entity)); } excelParams.addAll(entityList); sortAllParams(excelParams); this.index = entity.isCreateHeadRows() ? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0; titleHeight = index; setCellWith(excelParams, sheet); setColumnHidden(excelParams, sheet); rowHeight = getRowHeight(excelParams); setCurrentIndex(1); } catch (Exception e) { LOGGER.error(e.getMessage(), e); throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e.getCause()); } } public Workbook exportBigExcel(IExcelExportServer server, Object queryParams) { int page = 1; List list = server .selectListForExcelExport(queryParams, page++); while (list != null && list.size() > 0) { write(list); list = server.selectListForExcelExport(queryParams, page++); } return close(); } /** * 大数据导出 - 游标分页方式 * 推荐用于大数据量(20万+)导出,避免深分页性能问题 * for [QQYUN-13964]演示系统数据量大,点击没反应 * * 实现原理: * 1. 使用上一批次的最后一条记录作为查询起点 * 2. 避免 LIMIT offset, size 造成的性能衰减 * 3. 查询速度恒定,不会随着数据量增加而变慢 * * @param server 增强的查询服务 * @param queryParams 查询参数 * @return Workbook */ public Workbook exportBigExcelEnhanced(IExcelExportServerEnhanced server, Object queryParams) { int pageSize = server.getPageSize(); T lastRecord = null; int totalCount = 0; List list = server.selectListForExcelExport(queryParams, lastRecord, pageSize); while (list != null && list.size() > 0) { write(list); totalCount += list.size(); // 记录最后一条记录,用于下次查询 lastRecord = list.get(list.size() - 1); if (LOGGER.isDebugEnabled()) { LOGGER.debug("已导出 {} 条数据", totalCount); } // 如果返回的数据少于pageSize,说明已经是最后一批了 if (list.size() < pageSize) { break; } list = server.selectListForExcelExport(queryParams, lastRecord, pageSize); } LOGGER.info("大数据导出完成,共导出 {} 条数据", totalCount); return close(); } @Override public Workbook get() { return this.workbook; } @Override public IWriter write(Collection data) { if (sheet.getLastRowNum() + data.size() > entity.getMaxNum()) { sheet = workbook.createSheet(); index = 0; } Iterator its = data.iterator(); while (its.hasNext()) { Object t = its.next(); try { index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight, 0)[0]; } catch (Exception e) { LOGGER.error(e.getMessage(), e); throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e); } } return this; } @Override public Workbook close() { if (entity.getFreezeCol() != 0) { sheet.createFreezePane(entity.getFreezeCol(), titleHeight, entity.getFreezeCol(), titleHeight); } mergeCells(sheet, excelParams, titleHeight); // 创建合计信息 addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet); return workbook; } /** * 添加Index列 */ @Override public ExcelExportEntity indexExcelEntity(ExportParams entity) { ExcelExportEntity exportEntity = new ExcelExportEntity(); //保证是第一排 exportEntity.setOrderNum(Integer.MIN_VALUE); exportEntity.setNeedMerge(true); exportEntity.setName(entity.getIndexName()); exportEntity.setWidth(10); exportEntity.setFormat(PoiBaseConstants.IS_ADD_INDEX); return exportEntity; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/export/ExcelExportServer.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.export; import java.lang.reflect.Field; import java.util.*; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.jeecgframework.poi.excel.annotation.ExcelTarget; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.entity.enmus.ExcelType; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.jeecgframework.poi.excel.entity.vo.PoiBaseConstants; import org.jeecgframework.poi.excel.export.base.ExcelExportBase; import org.jeecgframework.poi.excel.export.styler.IExcelExportStyler; import org.jeecgframework.poi.exception.excel.ExcelExportException; import org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum; import org.jeecgframework.poi.util.PoiExcelGraphDataUtil; import org.jeecgframework.poi.util.PoiPublicUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Excel导出服务 * * @author JEECG * @date 2014年6月17日 下午5:30:54 */ public class ExcelExportServer extends ExcelExportBase { private final static Logger LOGGER = LoggerFactory.getLogger(ExcelExportServer.class); // 最大行数,超过自动多Sheet private int MAX_NUM = 60000; protected int createHeaderAndTitle(ExportParams entity, Sheet sheet, Workbook workbook, List excelParams) { int rows = 0, feildWidth = getFieldWidth(excelParams); if (entity.getTitle() != null) { rows += createHeaderRow(entity, sheet, workbook, feildWidth); } rows += createTitleRow(entity, sheet, workbook, rows, excelParams); //update-begin---author:liusq Date:20211217 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- if (entity.isFixedTitle()) { sheet.createFreezePane(0, rows, 0, rows); } //update-begin---author:liusq Date:20211217 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- return rows; } /** * 创建 表头改变 * * @param entity * @param sheet * @param workbook * @param feildWidth */ public int createHeaderRow(ExportParams entity, Sheet sheet, Workbook workbook, int feildWidth) { Row row = sheet.createRow(0); row.setHeight(entity.getTitleHeight()); createStringCell(row, 0, entity.getTitle(), getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null); for (int i = 1; i <= feildWidth; i++) { createStringCell(row, i, "", getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null); } //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B try { sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, feildWidth)); }catch (IllegalArgumentException e){ LOGGER.error("合并单元格错误日志:"+e.getMessage()); e.fillInStackTrace(); } //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B if (entity.getSecondTitle() != null) { row = sheet.createRow(1); row.setHeight(entity.getSecondTitleHeight()); //update-begin-author:liusq date:20230407 for:[issue/4342]autopoi导出带副标题的数据表,副标题缺左边框 CellStyle style = getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()); style.setAlignment(HorizontalAlignment.RIGHT); //update-end-author:liusq date:20230407 for:[issue/4342]autopoi导出带副标题的数据表,副标题缺左边框 createStringCell(row, 0, entity.getSecondTitle(), style, null); for (int i = 1; i <= feildWidth; i++) { createStringCell(row, i, "", getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null); } //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B try{ sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, feildWidth)); }catch (IllegalArgumentException e){ LOGGER.error("合并单元格错误日志:"+e.getMessage()); e.fillInStackTrace(); } //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B return 2; } return 1; } public void createSheet(Workbook workbook, ExportParams entity, Class pojoClass, Collection dataSet, String[] exportFields) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Excel export start ,class is {}", pojoClass); LOGGER.debug("Excel version is {}", entity.getType().equals(ExcelType.HSSF) ? "03" : "07"); } if (workbook == null || entity == null || pojoClass == null || dataSet == null) { throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR); } super.type = entity.getType(); if (type.equals(ExcelType.XSSF)) { MAX_NUM = 1000000; } Sheet sheet = null; try { sheet = workbook.createSheet(entity.getSheetName()); } catch (Exception e) { // 重复遍历,出现了重名现象,创建非指定的名称Sheet sheet = workbook.createSheet(); } try { dataHanlder = entity.getDataHanlder(); if (dataHanlder != null) { String[] needHandlerFields = dataHanlder.getNeedHandlerFields(); if(needHandlerFields!=null && needHandlerFields.length>0){ needHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields()); } } // 创建表格样式 setExcelExportStyler((IExcelExportStyler) entity.getStyle().getConstructor(Workbook.class).newInstance(workbook)); Drawing patriarch = sheet.createDrawingPatriarch(); List excelParams = new ArrayList(); if (entity.isAddIndex()) { excelParams.add(indexExcelEntity(entity)); } // 得到所有字段 Field fileds[] = PoiPublicUtil.getClassFields(pojoClass); //---update-begin-----autor:scott------date:20191016-------for:导出字段支持自定义-------- //支持自定义导出字段 if (exportFields != null) { List list = new ArrayList(Arrays.asList(fileds)); for (int i = 0; i < list.size(); i++) { if (!Arrays.asList(exportFields).contains(list.get(i).getName())) { list.remove(i); i--; } } if (list != null && list.size() > 0) { fileds = list.toArray(new Field[0]); } else { fileds = null; } } //---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义-------- ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class); String targetId = etarget == null ? null : etarget.value(); getAllExcelField(entity.getExclusions(), targetId, fileds, excelParams, pojoClass, null); //update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 rebuildDynamicColumns(dataSet, excelParams); //update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 //update-begin-author:taoyan date:20200304 for:在此方法循环内设置一下图片磁盘目录,便于导出 reConfigExcelExportParams(excelParams,entity); //update-end-author:taoyan date:20200304 for:在此方法循环内设置一下图片磁盘目录,便于导出 int index = entity.isCreateHeadRows() ? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0; int titleHeight = index; setCellWith(excelParams, sheet); //update-begin-author:liusq date:20210723 for:设置隐藏列 setColumnHidden(excelParams, sheet); //update-end-author:liusq date:20210723 for:设置隐藏列 short rowHeight = getRowHeight(excelParams); setCurrentIndex(1); Iterator its = dataSet.iterator(); List tempList = new ArrayList(); while (its.hasNext()) { Object t = its.next(); index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight); tempList.add(t); if (index >= MAX_NUM) break; } mergeCells(sheet, excelParams, titleHeight); if (entity.getFreezeCol() != 0) { sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0); } its = dataSet.iterator(); for (int i = 0, le = tempList.size(); i < le; i++) { its.next(); its.remove(); } // 创建合计信息 addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet); // 发现还有剩余list 继续循环创建Sheet if (dataSet.size() > 0) { createSheet(workbook, entity, pojoClass, dataSet,exportFields); } } catch (Exception e) { LOGGER.error(e.getMessage(), e); throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e); } } public void createSheetForMap(Workbook workbook, ExportParams entity, List entityList, Collection> dataSet) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Excel version is {}", entity.getType().equals(ExcelType.HSSF) ? "03" : "07"); } if (workbook == null || entity == null || entityList == null || dataSet == null) { throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR); } super.type = entity.getType(); if (type.equals(ExcelType.XSSF)) { MAX_NUM = 1000000; } Sheet sheet = null; try { sheet = workbook.createSheet(entity.getSheetName()); } catch (Exception e) { // 重复遍历,出现了重名现象,创建非指定的名称Sheet sheet = workbook.createSheet(); } try { dataHanlder = entity.getDataHanlder(); if (dataHanlder != null) { needHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields()); } // 创建表格样式 setExcelExportStyler((IExcelExportStyler) entity.getStyle().getConstructor(Workbook.class).newInstance(workbook)); Drawing patriarch = sheet.createDrawingPatriarch(); List excelParams = new ArrayList(); if (entity.isAddIndex()) { excelParams.add(indexExcelEntity(entity)); } excelParams.addAll(entityList); //update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 rebuildDynamicColumns(dataSet, excelParams); //update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 sortAllParams(excelParams); int index = entity.isCreateHeadRows() ? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0; int titleHeight = index; setCellWith(excelParams, sheet); //update-begin-author:liusq date:20210723 for:设置隐藏列 setColumnHidden(excelParams, sheet); //update-end-author:liusq date:20210723 for:设置隐藏列 short rowHeight = getRowHeight(excelParams); setCurrentIndex(1); Iterator its = dataSet.iterator(); List tempList = new ArrayList(); while (its.hasNext()) { Object t = its.next(); index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight); tempList.add(t); if (index >= MAX_NUM) break; } if (entity.getFreezeCol() != 0) { sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0); } mergeCells(sheet, excelParams, titleHeight); its = dataSet.iterator(); for (int i = 0, le = tempList.size(); i < le; i++) { its.next(); its.remove(); } // 发现还有剩余list 继续循环创建Sheet if (dataSet.size() > 0) { createSheetForMap(workbook, entity, entityList, dataSet); } } catch (Exception e) { LOGGER.error(e.getMessage(), e); e.printStackTrace(); // 添加打印完整堆栈信息 throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e); } } /** * 创建表头 * * @param title * @param index */ private int createTitleRow(ExportParams title, Sheet sheet, Workbook workbook, int index, List excelParams) { Row row = sheet.createRow(index); int rows = getRowNums(excelParams); row.setHeight((short) 450); Row listRow = null; if (rows == 2) { listRow = sheet.createRow(index + 1); listRow.setHeight((short) 450); } int cellIndex = 0; CellStyle titleStyle = getExcelExportStyler().getTitleStyle(title.getColor()); for (int i = 0, exportFieldTitleSize = excelParams.size(); i < exportFieldTitleSize; i++) { ExcelExportEntity entity = excelParams.get(i); //update-begin-author:taoyan date:20200319 for:建议autoPoi升级,优化数据返回List Map格式下的复合表头导出excel的体验 #873 if(entity.isColspan()){ List subList = entity.getSubColumnList(); if(subList==null || subList.size()==0){ continue; }else{ entity.initSubExportEntity(excelParams); } } //update-end-author:taoyan date:20200319 for:建议autoPoi升级,优化数据返回List Map格式下的复合表头导出excel的体验 #873 if (StringUtils.isNotBlank(entity.getName())) { createStringCell(row, cellIndex, entity.getName(), titleStyle, entity); } if (entity.getList() != null) { List sTitel = entity.getList(); //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B if (StringUtils.isNotBlank(entity.getName())) { try { sheet.addMergedRegion(new CellRangeAddress(index, index, cellIndex, cellIndex + sTitel.size() - 1)); }catch (IllegalArgumentException e){ LOGGER.error("合并单元格错误日志:"+e.getMessage()); e.fillInStackTrace(); } //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B } for (int j = 0, size = sTitel.size(); j < size; j++) { createStringCell(rows == 2 ? listRow : row, cellIndex, sTitel.get(j).getName(), titleStyle, entity); cellIndex++; } cellIndex--; } else if (rows == 2) { createStringCell(listRow, cellIndex, "", titleStyle, entity); //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B try{ sheet.addMergedRegion(new CellRangeAddress(index, index + 1, cellIndex, cellIndex)); }catch (IllegalArgumentException e){ LOGGER.error("合并单元格错误日志:"+e.getMessage()); e.fillInStackTrace(); } //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B } cellIndex++; } return rows; } /** * 判断表头是只有一行还是两行 * * @param excelParams * @return */ private int getRowNums(List excelParams) { for (int i = 0; i < excelParams.size(); i++) { //update-begin-author:taoyan date:20200319 for:建议autoPoi升级,优化数据返回List Map格式下的复合表头导出excel的体验 #873 ExcelExportEntity temp = excelParams.get(i); if ((temp.getList() != null || temp.isColspan()) && StringUtils.isNotBlank(temp.getName())) { return 2; } //update-end-author:taoyan date:20200319 for:建议autoPoi升级,优化数据返回List Map格式下的复合表头导出excel的体验 #873 } return 1; } protected ExcelExportEntity indexExcelEntity(ExportParams entity) { ExcelExportEntity exportEntity = new ExcelExportEntity(); exportEntity.setOrderNum(0); exportEntity.setName(entity.getIndexName()); exportEntity.setWidth(10); exportEntity.setFormat(PoiBaseConstants.IS_ADD_INDEX); return exportEntity; } //update-begin---author:liusq Date:20211217 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- /** * 添加数据到sheet * @param workbook * @param entity 导出参数 * @param entityList * @param dataSet 导出数据 * @param sheet * @date 2022年1月4号 */ protected void insertDataToSheet(Workbook workbook, ExportParams entity, List entityList,Collection> dataSet, Sheet sheet) { try { dataHanlder = entity.getDataHanlder(); if (dataHanlder != null && dataHanlder.getNeedHandlerFields() != null) { needHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields()); } // 创建表格样式 setExcelExportStyler((IExcelExportStyler) entity.getStyle() .getConstructor(Workbook.class).newInstance(workbook)); Drawing patriarch = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet); List excelParams = new ArrayList(); if (entity.isAddIndex()) { excelParams.add(indexExcelEntity(entity)); } excelParams.addAll(entityList); //update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 rebuildDynamicColumns(dataSet, excelParams); //update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 sortAllParams(excelParams); int index = entity.isCreateHeadRows() ? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0; int titleHeight = index; setCellWith(excelParams, sheet); setColumnHidden(excelParams, sheet); short rowHeight = entity.getHeight() != 0 ? entity.getHeight() : getRowHeight(excelParams); setCurrentIndex(1); Iterator its = dataSet.iterator(); List tempList = new ArrayList(); while (its.hasNext()) { Object t = its.next(); index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight, 0)[0]; tempList.add(t); if (index >= MAX_NUM) { break; } } if (entity.getFreezeCol() != 0) { sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0); } mergeCells(sheet, excelParams, titleHeight); its = dataSet.iterator(); for (int i = 0, le = tempList.size(); i < le; i++) { its.next(); its.remove(); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("List data more than max ,data size is {}", dataSet.size()); } // 发现还有剩余list 继续循环创建Sheet if (dataSet.size() > 0) { createSheetForMap(workbook, entity, entityList, dataSet); } else { // 创建合计信息 addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet); } } catch (Exception e) { LOGGER.error(e.getMessage(), e); e.printStackTrace(); // 添加打印完整堆栈信息 throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e); } } //update-end---author:liusq Date:20211217 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/export/base/ExcelExportBase.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.export.base; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.apache.poi.hssf.usermodel.HSSFClientAnchor; import org.apache.poi.hssf.usermodel.HSSFRichTextString; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.jeecgframework.poi.excel.entity.enmus.ExcelType; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.jeecgframework.poi.excel.entity.vo.PoiBaseConstants; import org.jeecgframework.poi.excel.export.styler.IExcelExportStyler; import org.jeecgframework.poi.exception.excel.ExcelExportException; import org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum; import org.jeecgframework.poi.util.MyX509TrustManager; import org.jeecgframework.poi.util.PoiMergeCellUtil; import org.jeecgframework.poi.util.PoiPublicUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.security.SecureRandom; import java.text.DecimalFormat; import java.util.*; /** * 提供POI基础操作服务 * * @author JEECG * @date 2014年6月17日 下午6:15:13 */ public abstract class ExcelExportBase extends ExportBase { private static final Logger LOGGER = LoggerFactory.getLogger(ExcelExportBase.class); private int currentIndex = 0; protected ExcelType type = ExcelType.HSSF; private Map statistics = new HashMap(); private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); /** * 常规格式的内置格式标识符 * for [issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248 */ private static final short GENERAL_FORMAT = (short)BuiltinFormats.getBuiltinFormat("General"); /** * 单元格样式缓存 * for [issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248 */ private final Map cellStyleMap = new HashMap<>(); //update-begin-author:liusq---date:20220527--for: 修改成protected,列循环时继承类需要用到 --- protected IExcelExportStyler excelExportStyler; //update-end-author:liusq---date:20220527--for: 修改成protected,列循环时继承类需要用到 --- /** * 创建 最主要的 Cells * * @param styles * @param rowHeight * @throws Exception */ public int createCells(Drawing patriarch, int index, Object t, List excelParams, Sheet sheet, Workbook workbook, short rowHeight) throws Exception { ExcelExportEntity entity; Row row = sheet.createRow(index); DataFormat df = workbook.createDataFormat(); row.setHeight(rowHeight); int maxHeight = 1, cellNum = 0; int indexKey = createIndexCell(row, index, excelParams.get(0)); cellNum += indexKey; for (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) { entity = excelParams.get(k); //update-begin-author:taoyan date:20200319 for:建议autoPoi升级,优化数据返回List Map格式下的复合表头导出excel的体验 #873 if(entity.isSubColumn()){ continue; } if(entity.isMergeColumn()){ Map subColumnMap = new HashMap<>(); List mapKeys = entity.getSubColumnList(); for (String subKey : mapKeys) { Object subKeyValue = null; if (t instanceof Map) { subKeyValue = ((Map) t).get(subKey); }else{ subKeyValue = PoiPublicUtil.getParamsValue(subKey,t); } subColumnMap.put(subKey,subKeyValue); } createListCells(patriarch, index, cellNum, subColumnMap, entity.getList(), sheet, workbook); cellNum += entity.getSubColumnList().size(); //update-end-author:taoyan date:20200319 for:建议autoPoi升级,优化数据返回List Map格式下的复合表头导出excel的体验 #873 } else if (entity.getList() != null) { Collection list = getListCellValue(entity, t); int listC = 0; for (Object obj : list) { createListCells(patriarch, index + listC, cellNum, obj, entity.getList(), sheet, workbook); listC++; } cellNum += entity.getList().size(); if (list != null && list.size() > maxHeight) { maxHeight = list.size(); } } else { Object value = getCellValue(entity, t); //update-begin--Author:xuelin Date:20171018 for:TASK #2372 【excel】AutoPoi 导出类型,type增加数字类型-------------------- if (entity.getType() == 1) { createStringCell(row, cellNum++, value == null ? "" : value.toString(), index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity), entity); } else if (entity.getType() == 4){ createNumericCell(row, cellNum++, value == null ? "" : value.toString(), getNumberCellStyle(index, df, entity), entity); } else if (entity.getType() == 0) { //update-begin---author:chenrui ---date:20250604 for:[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------ createStringCell(row, cellNum++, value == null ? "" : value.toString(), getGeneralCellStyle(index, workbook,entity), entity); //update-end---author:chenrui ---date:20250604 for:[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------ } else { createImageCell(patriarch, entity, row, cellNum++, value == null ? "" : value.toString(), t); } //update-end--Author:xuelin Date:20171018 for:TASK #2372 【excel】AutoPoi 导出类型,type增加数字类型-------------------- //update-begin-author:liusq---date:20220728--for:[issues/I5I840] @Excel注解中不支持超链接,但文档中支持 --- if (entity.isHyperlink()) { row.getCell(cellNum - 1) .setHyperlink(dataHanlder.getHyperlink( row.getSheet().getWorkbook().getCreationHelper(), t, entity.getName(), value)); } //update-end-author:liusq---date:20220728--for:[issues/I5I840] @Excel注解中不支持超链接,但文档中支持 --- } } // 合并需要合并的单元格 cellNum = 0; for (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) { entity = excelParams.get(k); if (entity.getList() != null) { cellNum += entity.getList().size(); } else if (entity.isNeedMerge()) { for (int i = index + 1; i < index + maxHeight; i++) { sheet.getRow(i).createCell(cellNum); sheet.getRow(i).getCell(cellNum).setCellStyle(getStyles(false, entity)); } //update-begin-author:wangshuai date:20201116 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B try { if (maxHeight > 1) { sheet.addMergedRegion(new CellRangeAddress(index, index + maxHeight - 1, cellNum, cellNum)); } }catch (IllegalArgumentException e){ LOGGER.error("合并单元格错误日志:"+e.getMessage()); e.fillInStackTrace(); } //update-end-author:wangshuai date:20201116 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B cellNum++; } } return maxHeight; } /** * 获取数值单元格样式 * @param index * @param df * @param entity * @return */ private CellStyle getNumberCellStyle(int index,DataFormat df, ExcelExportEntity entity) { //update-begin-author:liusq---date:2023-12-07--for: [issues/5538]导出表格设置了数字格式导出之后仍然是文本格式,并且无法进行计算 CellStyle cellStyle = index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity); String numFormat = StringUtils.isNotBlank(entity.getNumFormat())? entity.getNumFormat():"0.00_ "; cellStyle.setDataFormat(df.getFormat(numFormat)); return cellStyle; //update-end-author:liusq---date:2023-12-07--for:[issues/5538]导出表格设置了数字格式导出之后仍然是文本格式,并且无法进行计算 } /** * 获取常规单元格样式 * for [issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248 * @param index * @param workbook * @param entity * @return * @author chenrui * @date 2025/6/4 17:40 */ private CellStyle getGeneralCellStyle(int index, Workbook workbook, ExcelExportEntity entity) { String cellStyleKey = index % 2 == 0 ? "twoGeneral" : "oneGeneral"; if (this.cellStyleMap.containsKey(cellStyleKey)) { return this.cellStyleMap.get(cellStyleKey); } CellStyle cellStyle = index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity); CellStyle newStyle = workbook.createCellStyle(); newStyle.cloneStyleFrom(cellStyle); newStyle.setDataFormat(GENERAL_FORMAT); this.cellStyleMap.put(cellStyleKey, newStyle); return newStyle; } /** * 通过https地址获取图片数据 * @param imagePath * @return * @throws Exception */ private byte[] getImageDataByHttps(String imagePath) throws Exception { SSLContext sslcontext = SSLContext.getInstance("SSL","SunJSSE"); sslcontext.init(null, new TrustManager[]{new MyX509TrustManager()}, new SecureRandom()); URL url = new URL(imagePath); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.setSSLSocketFactory(sslcontext.getSocketFactory()); conn.setRequestMethod("GET"); conn.setConnectTimeout(5 * 1000); InputStream inStream = conn.getInputStream(); byte[] value = readInputStream(inStream); return value; } /** * 通过http地址获取图片数据 * @param imagePath * @return * @throws Exception */ private byte[] getImageDataByHttp(String imagePath) throws Exception { URL url = new URL(imagePath); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5 * 1000); InputStream inStream = conn.getInputStream(); byte[] value = readInputStream(inStream); return value; } /** * 图片类型的Cell * * @param patriarch * @param entity * @param row * @param i * @param imagePath * @param obj * @throws Exception */ public void createImageCell(Drawing patriarch, ExcelExportEntity entity, Row row, int i, String imagePath, Object obj) throws Exception { row.setHeight((short) (50 * entity.getHeight())); row.createCell(i); ClientAnchor anchor; if (type.equals(ExcelType.HSSF)) { anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) i, row.getRowNum(), (short) (i + 1), row.getRowNum() + 1); } else { anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) i, row.getRowNum(), (short) (i + 1), row.getRowNum() + 1); } if (StringUtils.isEmpty(imagePath)) { return; } //update-beign-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159 int imageType = entity.getExportImageType(); byte[] value = null; if(imageType == 2){ //原来逻辑 2 value = (byte[]) (entity.getMethods() != null ? getFieldBySomeMethod(entity.getMethods(), obj) : entity.getMethod().invoke(obj, new Object[] {})); } else if(imageType==4 || imagePath.startsWith("http")){ //新增逻辑 网络图片4 try { if (imagePath.indexOf(",") != -1) { if(imagePath.startsWith(",")){ imagePath = imagePath.substring(1); } String[] images = imagePath.split(","); imagePath = images[0]; } if(imagePath.startsWith("https")){ value = getImageDataByHttps(imagePath); }else{ value = getImageDataByHttp(imagePath); } } catch (Exception exception) { LOGGER.warn(exception.getMessage()); //exception.printStackTrace(); } } else { ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); BufferedImage bufferImg; String path = null; if(imageType == 1){ //原来逻辑 1 path = PoiPublicUtil.getWebRootPath(imagePath); LOGGER.debug("--- createImageCell getWebRootPath ----filePath--- "+ path); path = path.replace("WEB-INF/classes/", ""); path = path.replace("file:/", ""); }else if(imageType==3){ //新增逻辑 本地图片3 //begin-------author:liusq---data:2021-01-27----for:本地图片ImageBasePath为空报错的问题 if(StringUtils.isNotBlank(entity.getImageBasePath())){ if(!entity.getImageBasePath().endsWith(File.separator) && !imagePath.startsWith(File.separator)){ path = entity.getImageBasePath()+File.separator+imagePath; }else{ path = entity.getImageBasePath()+imagePath; } }else{ path = imagePath; } //end-------author:liusq---data:2021-01-27----for:本地图片ImageBasePath为空报错的问题 } try { bufferImg = ImageIO.read(new File(path)); //update-begin-author:taoYan date:20211203 for: Excel 导出图片的文件带小数点符号 导出报错 https://gitee.com/jeecg/jeecg-boot/issues/I4JNHR ImageIO.write(bufferImg, imagePath.substring(imagePath.lastIndexOf(".") + 1, imagePath.length()), byteArrayOut); //update-end-author:taoYan date:20211203 for: Excel 导出图片的文件带小数点符号 导出报错 https://gitee.com/jeecg/jeecg-boot/issues/I4JNHR value = byteArrayOut.toByteArray(); } catch (Exception e) { LOGGER.error(e.getMessage()); } } if (value != null) { patriarch.createPicture(anchor, row.getSheet().getWorkbook().addPicture(value, getImageType(value))); } //update-end-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159 } /** * inStream读取到字节数组 * @param inStream * @return * @throws Exception */ private byte[] readInputStream(InputStream inStream) throws Exception { if(inStream==null){ return null; } ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; //每次读取的字符串长度,如果为-1,代表全部读取完毕 while ((len = inStream.read(buffer)) != -1) { outStream.write(buffer, 0, len); } inStream.close(); return outStream.toByteArray(); } private int createIndexCell(Row row, int index, ExcelExportEntity excelExportEntity) { if (excelExportEntity.getName().equals("序号") && PoiBaseConstants.IS_ADD_INDEX.equals(excelExportEntity.getFormat())) { createStringCell(row, 0, currentIndex + "", index % 2 == 0 ? getStyles(false, null) : getStyles(true, null), null); currentIndex = currentIndex + 1; return 1; } return 0; } /** * 创建List之后的各个Cells * @param patriarch * @param index * @param cellNum * @param obj * @param excelParams * @param sheet * @param workbook * @throws Exception */ public void createListCells(Drawing patriarch, int index, int cellNum, Object obj, List excelParams, Sheet sheet, Workbook workbook) throws Exception { ExcelExportEntity entity; Row row; DataFormat df = workbook.createDataFormat(); if (sheet.getRow(index) == null) { row = sheet.createRow(index); row.setHeight(getRowHeight(excelParams)); } else { row = sheet.getRow(index); } for (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) { entity = excelParams.get(k); Object value = getCellValue(entity, obj); //update-begin--Author:xuelin Date:20171018 for:TASK #2372 【excel】AutoPoi 导出类型,type增加数字类型-------------------- if (entity.getType() == 1) { createStringCell(row, cellNum++, value == null ? "" : value.toString(), row.getRowNum() % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity), entity); //update-begin-author:liusq---date:20220728--for: 新增isHyperlink属性 --- if (entity.isHyperlink()) { row.getCell(cellNum - 1) .setHyperlink(dataHanlder.getHyperlink( row.getSheet().getWorkbook().getCreationHelper(), obj, entity.getName(), value)); } //update-end-author:liusq---date:20220728--for: 新增isHyperlink属性 --- } else if (entity.getType() == 4){ createNumericCell(row, cellNum++, value == null ? "" : value.toString(), getNumberCellStyle(index, df, entity), entity); //update-begin-author:liusq---date:20220728--for: 新增isHyperlink属性 --- if (entity.isHyperlink()) { row.getCell(cellNum - 1) .setHyperlink(dataHanlder.getHyperlink( row.getSheet().getWorkbook().getCreationHelper(), obj, entity.getName(), value)); } //update-end-author:liusq---date:20220728--for: 新增isHyperlink属性 --- } else if (entity.getType() == 0) { //update-begin---author:chenrui ---date:20250604 for:[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------ createStringCell(row, cellNum++, value == null ? "" : value.toString(), getGeneralCellStyle(index, workbook, entity), entity); if (entity.isHyperlink()) { row.getCell(cellNum - 1) .setHyperlink(dataHanlder.getHyperlink( row.getSheet().getWorkbook().getCreationHelper(), obj, entity.getName(), value)); } //update-end---author:chenrui ---date:20250604 for:[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------ } else{ createImageCell(patriarch, entity, row, cellNum++, value == null ? "" : value.toString(), obj); } //update-end--Author:xuelin Date:20171018 for:TASK #2372 【excel】AutoPoi 导出类型,type增加数字类型-------------------- } } //update-begin--Author:xuelin Date:20171018 for:TASK #2372 【excel】AutoPoi 导出类型,type增加数字类型-------------------- public void createNumericCell (Row row, int index, String text, CellStyle style, ExcelExportEntity entity) { Cell cell = row.createCell(index); if (style != null) { cell.setCellStyle(style); } if(StringUtils.isEmpty(text)){ cell.setCellValue(""); cell.setCellType(CellType.BLANK); }else{ cell.setCellValue(Double.parseDouble(text)); cell.setCellType(CellType.NUMERIC); } addStatisticsData(index, text, entity); } /** * 创建文本类型的Cell * * @param row * @param index * @param text * @param style * @param entity */ public void createStringCell(Row row, int index, String text, CellStyle style, ExcelExportEntity entity) { Cell cell = row.createCell(index); if (style != null && style.getDataFormat() > 0 && style.getDataFormat() < 12) { cell.setCellValue(Double.parseDouble(text)); cell.setCellType(CellType.NUMERIC); }else{ RichTextString Rtext; if (type.equals(ExcelType.HSSF)) { Rtext = new HSSFRichTextString(text); } else { Rtext = new XSSFRichTextString(text); } cell.setCellValue(Rtext); } if (style != null) { cell.setCellStyle(style); } addStatisticsData(index, text, entity); } /** * 设置字段下划线 * @param row * @param index * @param text * @param style * @param entity * @param workbook */ /*public void createStringCell(Row row, int index, String text, CellStyle style, ExcelExportEntity entity, Workbook workbook) { Cell cell = row.createCell(index); if (style != null && style.getDataFormat() > 0 && style.getDataFormat() < 12) { cell.setCellValue(Double.parseDouble(text)); cell.setCellType(CellType.NUMERIC); }else{ RichTextString Rtext; if (type.equals(ExcelType.HSSF)) { Rtext = new HSSFRichTextString(text); } else { Rtext = new XSSFRichTextString(text); } cell.setCellValue(Rtext); } if (style != null) { Font font = workbook.createFont(); font.setUnderline(Font.U_SINGLE); style.setFont(font); cell.setCellStyle(style); } addStatisticsData(index, text, entity); }*/ //update-end--Author:xuelin Date:20171018 for:TASK #2372 【excel】AutoPoi 导出类型,type增加数字类型---------------------- /** * 创建统计行 * * @param styles * @param sheet */ public void addStatisticsRow(CellStyle styles, Sheet sheet) { if (statistics.size() > 0) { Row row = sheet.createRow(sheet.getLastRowNum() + 1); Set keys = statistics.keySet(); createStringCell(row, 0, "合计", styles, null); for (Integer key : keys) { createStringCell(row, key, DOUBLE_FORMAT.format(statistics.get(key)), styles, null); } statistics.clear(); } } /** * 合计统计信息 * * @param index * @param text * @param entity */ private void addStatisticsData(Integer index, String text, ExcelExportEntity entity) { if (entity != null && entity.isStatistics()) { Double temp = 0D; if (!statistics.containsKey(index)) { statistics.put(index, temp); } try { temp = Double.valueOf(text); } catch (NumberFormatException e) { } statistics.put(index, statistics.get(index) + temp); } } /** * 获取导出报表的字段总长度 * * @param excelParams * @return */ public int getFieldWidth(List excelParams) { int length = -1;// 从0开始计算单元格的 for (ExcelExportEntity entity : excelParams) { //update-begin---author:liusq Date:20200909 for:AutoPoi多表头导出,会多出一列空白列 #1513------------ if(entity.getGroupName()!=null){ continue; }else if (entity.getSubColumnList()!=null&&entity.getSubColumnList().size()>0){ length += entity.getSubColumnList().size(); }else{ length += entity.getList() != null ? entity.getList().size() : 1; } //update-end---author:liusq Date:20200909 for:AutoPoi多表头导出,会多出一列空白列 #1513------------ } return length; } /** * 获取图片类型,设置图片插入类型 * * @param value * @return * @Author JEECG * @date 2013年11月25日 */ public int getImageType(byte[] value) { String type = PoiPublicUtil.getFileExtendName(value); if (type.equalsIgnoreCase("JPG")) { return Workbook.PICTURE_TYPE_JPEG; } else if (type.equalsIgnoreCase("PNG")) { return Workbook.PICTURE_TYPE_PNG; } return Workbook.PICTURE_TYPE_JPEG; } private Map getMergeDataMap(List excelParams) { Map mergeMap = new HashMap(); // 设置参数顺序,为之后合并单元格做准备 int i = 0; for (ExcelExportEntity entity : excelParams) { if (entity.isMergeVertical()) { mergeMap.put(i, entity.getMergeRely()); } if (entity.getList() != null) { for (ExcelExportEntity inner : entity.getList()) { if (inner.isMergeVertical()) { mergeMap.put(i, inner.getMergeRely()); } i++; } } else { i++; } } return mergeMap; } /** * 获取样式 * * @param entity * @param needOne * @return */ public CellStyle getStyles(boolean needOne, ExcelExportEntity entity) { return excelExportStyler.getStyles(needOne, entity); } /** * 合并单元格 * * @param sheet * @param excelParams * @param titleHeight */ public void mergeCells(Sheet sheet, List excelParams, int titleHeight) { Map mergeMap = getMergeDataMap(excelParams); PoiMergeCellUtil.mergeCells(sheet, mergeMap, titleHeight); } public void setCellWith(List excelParams, Sheet sheet) { int index = 0; for (int i = 0; i < excelParams.size(); i++) { if (excelParams.get(i).getList() != null) { List list = excelParams.get(i).getList(); for (int j = 0; j < list.size(); j++) { sheet.setColumnWidth(index, (int) (256 * list.get(j).getWidth())); index++; } } else { sheet.setColumnWidth(index, (int) (256 * excelParams.get(i).getWidth())); index++; } } } /** * 设置隐藏列 * @param excelParams * @param sheet */ public void setColumnHidden(List excelParams, Sheet sheet) { int index = 0; for (int i = 0; i < excelParams.size(); i++) { if (excelParams.get(i).getList() != null) { List list = excelParams.get(i).getList(); for (int j = 0; j < list.size(); j++) { sheet.setColumnHidden(index, list.get(j).isColumnHidden()); index++; } } else { sheet.setColumnHidden(index, excelParams.get(i).isColumnHidden()); index++; } } } public void setCurrentIndex(int currentIndex) { this.currentIndex = currentIndex; } public void setExcelExportStyler(IExcelExportStyler excelExportStyler) { this.excelExportStyler = excelExportStyler; } public IExcelExportStyler getExcelExportStyler() { return excelExportStyler; } //update-begin---author:liusq Date:20211217 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- /** *创建单元格,返回最大高度和单元格数 * @param patriarch * @param index * @param t * @param excelParams * @param sheet * @param workbook * @param rowHeight 行高 * @param cellNum 格数 * @return */ public int[] createCells(Drawing patriarch, int index, Object t, List excelParams, Sheet sheet, Workbook workbook, short rowHeight, int cellNum) { try { ExcelExportEntity entity; Row row = sheet.getRow(index) == null ? sheet.createRow(index) : sheet.getRow(index); DataFormat df = workbook.createDataFormat(); if (rowHeight != -1) { row.setHeight(rowHeight); } int maxHeight = 1, listMaxHeight = 1; // 合并需要合并的单元格 int margeCellNum = cellNum; int indexKey = 0; if (excelParams != null && !excelParams.isEmpty()) { indexKey = createIndexCell(row, index, excelParams.get(0)); } cellNum += indexKey; for (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) { entity = excelParams.get(k); //不论数据是否为空都应该把该列的数据跳过去 if (entity.getList() != null) { Collection list = getListCellValue(entity, t); int tmpListHeight = 0; if (list != null && list.size() > 0) { int tempCellNum = 0; for (Object obj : list) { int[] temp = createCells(patriarch, index + tmpListHeight, obj, entity.getList(), sheet, workbook, rowHeight, cellNum); tempCellNum = temp[1]; tmpListHeight += temp[0]; } cellNum = tempCellNum; listMaxHeight = Math.max(listMaxHeight, tmpListHeight); } else { cellNum = cellNum + getListCellSize(entity.getList()); } } else { Object value = getCellValue(entity, t); if (entity.getType() == 1) { createStringCell(row, cellNum++, value == null ? "" : value.toString(), index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity), entity); } else if (entity.getType() == 4) { createNumericCell(row, cellNum++, value == null ? "" : value.toString(), getNumberCellStyle(index, df, entity), entity); } else if (entity.getType() == 0) { //update-begin---author:chenrui ---date:20250604 for:[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------ createStringCell(row, cellNum++, value == null ? "" : value.toString(), getGeneralCellStyle(index, workbook, entity), entity); //update-end---author:chenrui ---date:20250604 for:[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------ } else { createImageCell(patriarch, entity, row, cellNum++, value == null ? "" : value.toString(), t); } //update-begin-author:liusq---date:20220728--for: 新增isHyperlink属性 --- if (entity.isHyperlink()) { row.getCell(cellNum - 1) .setHyperlink(dataHanlder.getHyperlink( row.getSheet().getWorkbook().getCreationHelper(), t, entity.getName(), value)); } //update-end-author:liusq---date:20220728--for: 新增isHyperlink属性 --- } } maxHeight += listMaxHeight - 1; if (indexKey == 1 && excelParams.get(1).isNeedMerge()) { excelParams.get(0).setNeedMerge(true); } for (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) { entity = excelParams.get(k); if (entity.getList() != null) { margeCellNum += entity.getList().size(); } else if (entity.isNeedMerge() && maxHeight > 1) { for (int i = index + 1; i < index + maxHeight; i++) { if (sheet.getRow(i) == null) { sheet.createRow(i); } sheet.getRow(i).createCell(margeCellNum); sheet.getRow(i).getCell(margeCellNum).setCellStyle(getStyles(false, entity)); } PoiMergeCellUtil.addMergedRegion(sheet, index, index + maxHeight - 1, margeCellNum, margeCellNum); margeCellNum++; } } return new int[]{maxHeight, cellNum}; } catch (Exception e) { LOGGER.error("excel cell export error ,data is :{}", ReflectionToStringBuilder.toString(t)); LOGGER.error(e.getMessage(), e); throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e); } } /** * 获取集合的宽度 * * @param list * @return */ protected int getListCellSize(List list) { int cellSize = 0; for (ExcelExportEntity ee : list) { if (ee.getList() != null) { cellSize += getListCellSize(ee.getList()); } else { cellSize++; } } return cellSize; } //update-end---author:liusq Date:20211217 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/export/base/ExportBase.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.export.base; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import com.google.common.collect.Lists; import org.apache.commons.lang3.StringUtils; import org.jeecgframework.core.util.ApplicationContextUtil; import org.jeecgframework.dict.service.AutoPoiDictServiceI; import org.jeecgframework.poi.excel.annotation.Excel; import org.jeecgframework.poi.excel.annotation.ExcelCollection; import org.jeecgframework.poi.excel.annotation.ExcelEntity; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.jeecgframework.poi.handler.inter.IExcelDataHandler; import org.jeecgframework.poi.handler.inter.IExcelDictHandler; import org.jeecgframework.poi.util.JsonParser; import org.jeecgframework.poi.util.PoiPublicUtil; /** * 导出基础处理,不设计POI,只设计对象,保证复用性 * * @author JEECG * @date 2014年8月9日 下午11:01:32 */ public class ExportBase { protected IExcelDataHandler dataHanlder; //update-begin-author:liusq---date:20220527--for: 增加列循环功能时中用到 --- protected IExcelDictHandler dictHandler; //update-end-author:liusq---date:20220527--for: be 增加列循环功能时中用到--- protected List needHanlderList; /** * 创建导出实体对象 * * @param field * @param targetId * @param pojoClass * @param getMethods * @return * @throws Exception */ private ExcelExportEntity createExcelExportEntity(Field field, String targetId, Class pojoClass, List getMethods) throws Exception { Excel excel = field.getAnnotation(Excel.class); ExcelExportEntity excelEntity = new ExcelExportEntity(); excelEntity.setType(excel.type()); getExcelField(targetId, field, excelEntity, excel, pojoClass); if (getMethods != null) { List newMethods = new ArrayList(); newMethods.addAll(getMethods); newMethods.add(excelEntity.getMethod()); excelEntity.setMethods(newMethods); } return excelEntity; } private Object formatValue(Object value, ExcelExportEntity entity) throws Exception { Date temp = null; //update-begin-author:wangshuai date:20201118 for:Excel导出错误原因,value为""字符串,gitee I249JF if("".equals(value)){ value= null; } //update-begin-author:wangshuai date:20201118 for:Excel导出错误原因,value为""字符串,gitee I249JF if (value instanceof String && entity.getDatabaseFormat()!=null) { SimpleDateFormat format = new SimpleDateFormat(entity.getDatabaseFormat()); temp = format.parse(value.toString()); } else if (value instanceof Date) { temp = (Date) value; //update-begin-author:taoyan date:2022-5-17 for: mybatis-plus升级 时间字段变成了jdk8的LocalDateTime,导致格式化失败 } else if (value instanceof LocalDateTime) { LocalDateTime ldt = (LocalDateTime) value; DateTimeFormatter format = DateTimeFormatter.ofPattern(entity.getFormat()); return format.format(ldt); } else if (value instanceof LocalDate) { LocalDate ld = (LocalDate) value; DateTimeFormatter format = DateTimeFormatter.ofPattern(entity.getFormat()); return format.format(ld); } //update-end-author:taoyan date:2022-5-17 for: mybatis-plus升级 时间字段变成了jdk8的LocalDateTime,导致格式化失败 if (temp != null) { SimpleDateFormat format = new SimpleDateFormat(entity.getFormat()); value = format.format(temp); } return value; } /** * 获取需要导出的全部字段 * * @param exclusions * @param targetId * 目标ID * @param fields * @throws Exception */ public void getAllExcelField(String[] exclusions, String targetId, Field[] fields, List excelParams, Class pojoClass, List getMethods) throws Exception { List exclusionsList = exclusions != null ? Arrays.asList(exclusions) : null; ExcelExportEntity excelEntity; // 遍历整个filed for (int i = 0; i < fields.length; i++) { Field field = fields[i]; // 先判断是不是collection,在判断是不是java自带对象,之后就是我们自己的对象了 if (PoiPublicUtil.isNotUserExcelUserThis(exclusionsList, field, targetId)) { continue; } // 首先判断Excel 可能一下特殊数据用户回自定义处理 if (field.getAnnotation(Excel.class) != null) { excelParams.add(createExcelExportEntity(field, targetId, pojoClass, getMethods)); } else if (PoiPublicUtil.isCollection(field.getType())) { ExcelCollection excel = field.getAnnotation(ExcelCollection.class); ParameterizedType pt = (ParameterizedType) field.getGenericType(); Class clz = (Class) pt.getActualTypeArguments()[0]; List list = new ArrayList(); getAllExcelField(exclusions, StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId, PoiPublicUtil.getClassFields(clz), list, clz, null); excelEntity = new ExcelExportEntity(); excelEntity.setName(getExcelName(excel.name(), targetId)); excelEntity.setOrderNum(getCellOrder(excel.orderNum(), targetId)); excelEntity.setMethod(PoiPublicUtil.getMethod(field.getName(), pojoClass)); excelEntity.setList(list); excelParams.add(excelEntity); } else { List newMethods = new ArrayList(); if (getMethods != null) { newMethods.addAll(getMethods); } newMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass)); ExcelEntity excel = field.getAnnotation(ExcelEntity.class); //update-begin-author:taoyan date:20210531 for:excel导出支持 注解@ExcelEntity显示合并表头 if(excel.show()==true){ List list = new ArrayList(); // 这里有个设计的坑,导出的时候最后一个参数是null, 即getgetMethods获取的是空,导入的时候需要设置层级getmethod getAllExcelField(exclusions, StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId, PoiPublicUtil.getClassFields(field.getType()), list, field.getType(), null); excelEntity = new ExcelExportEntity(); excelEntity.setName(getExcelName(excel.name(), targetId)); excelEntity.setMethod(PoiPublicUtil.getMethod(field.getName(), pojoClass)); excelEntity.setList(list); excelParams.add(excelEntity); }else{ getAllExcelField(exclusions, StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId, PoiPublicUtil.getClassFields(field.getType()), excelParams, field.getType(), newMethods); } //update-end-author:taoyan date:20210531 for:excel导出支持 注解@ExcelEntity显示合并表头 } } } /** * 获取这个字段的顺序 * * @param orderNum * @param targetId * @return */ public int getCellOrder(String orderNum, String targetId) { if (isInteger(orderNum) || targetId == null) { return Integer.valueOf(orderNum); } String[] arr = orderNum.split(","); String[] temp; for (String str : arr) { temp = str.split("_"); if (targetId.equals(temp[1])) { return Integer.valueOf(temp[0]); } } return 0; } /** * 获取填如这个cell的值,提供一些附加功能 * * @param entity * @param obj * @return * @throws Exception */ public Object getCellValue(ExcelExportEntity entity, Object obj) throws Exception { Object value; //update-begin---author:chenrui ---date:20250819 for:[issues/8699]AutoPoi在使用@ExcelEntity当设置show=true并且该项为null时报错------------ if(null == obj){ return ""; } //update-end---author:chenrui ---date:20250819 for:[issues/8699]AutoPoi在使用@ExcelEntity当设置show=true并且该项为null时报错------------ //update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 if (entity.isDynamic()) { value = getDynamicCellValue(entity, obj); //update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 }else if (obj instanceof Map) { value = ((Map) obj).get(entity.getKey()); } else { value = entity.getMethods() != null ? getFieldBySomeMethod(entity.getMethods(), obj) : entity.getMethod().invoke(obj, new Object[] {}); } //update-begin-author:scott date:20200831 for:导出excel实体反射,时间格式转换错误 #1573 value = Optional.ofNullable(value).orElse(""); if (StringUtils.isEmpty(value.toString())) { return ""; } //update-end-author:scott date:20200831 for:导出excel实体反射,时间格式转换错误 #1573 //update-begin-author:taoyan date:2020319 for:Excel注解的numFormat方法似乎未实现 #970 if (StringUtils.isNotEmpty(entity.getNumFormat()) && value!=null) { value = new DecimalFormat(entity.getNumFormat()).format(value); } //update-end-author:taoyan date:2020319 for:Excel注解的numFormat方法似乎未实现 #970 if (StringUtils.isNotEmpty(entity.getDict()) && dictHandler != null) { value = dictHandler.toName(entity.getDict(), obj, entity.getName(), value); } if (StringUtils.isNotEmpty(entity.getFormat())) { value = formatValue(value, entity); } if (entity.getReplace() != null && entity.getReplace().length > 0) { //update-begin-author:taoyan date:20180731 for:TASK #3038 【bug】Excel 导出多个值(逗号隔开的情况下,导出字典值是ID值) if(value == null){ value = "";//String.valueOf(value) 如果value为null 则返回"null" } String oldVal=value.toString(); if(entity.isMultiReplace()){ value = multiReplaceValue(entity.getReplace(), String.valueOf(value)); }else{ value = replaceValue(entity.getReplace(), String.valueOf(value)); } //update-end-author:taoyan date:20180731 for:TASK #3038 【bug】Excel 导出多个值(逗号隔开的情况下,导出字典值是ID值) //update-begin-author:liusq date:20210127 for: 两个数值相等,就证明处理翻译失败的情况 if(oldVal.equals(value)){ } //update-end-author:liusq date:20210127 for: 两个数值相等,就证明处理翻译失败的情况 } if (needHanlderList != null && needHanlderList.contains(entity.getName())) { value = dataHanlder.exportHandler(obj, entity.getName(), value); } if (StringUtils.isNotEmpty(entity.getSuffix()) && value != null) { value = value + entity.getSuffix(); } return value == null ? "" : value.toString(); } /** * 获取集合的值 * * @param entity * @param obj * @return * @throws Exception */ public Collection getListCellValue(ExcelExportEntity entity, Object obj) throws Exception { Object value; if (obj instanceof Map) { value = ((Map) obj).get(entity.getKey()); } else { value = entity.getMethod().invoke(obj, new Object[] {}); if(value instanceof Collection){ return (Collection)value; }else{ List list = new ArrayList(); list.add(value); return list; } } return (Collection) value; } /** * 注解到导出对象的转换 * * @param targetId * @param field * @param excelEntity * @param excel * @param pojoClass * @throws Exception */ private void getExcelField(String targetId, Field field, ExcelExportEntity excelEntity, Excel excel, Class pojoClass) throws Exception { excelEntity.setName(getExcelName(excel.name(), targetId)); excelEntity.setWidth(excel.width()); excelEntity.setHeight(excel.height()); excelEntity.setNeedMerge(excel.needMerge()); excelEntity.setMergeVertical(excel.mergeVertical()); excelEntity.setMergeRely(excel.mergeRely()); excelEntity.setReplace(excel.replace()); excelEntity.setHyperlink(excel.isHyperlink()); if(StringUtils.isNotEmpty(excel.dicCode())){ AutoPoiDictServiceI jeecgDictService = null; try { jeecgDictService = ApplicationContextUtil.getContext().getBean(AutoPoiDictServiceI.class); } catch (Exception e) { } if(jeecgDictService!=null){ String[] dictReplace = jeecgDictService.queryDict(excel.dictTable(), excel.dicCode(), excel.dicText()); if(excelEntity.getReplace()!=null && dictReplace!=null && dictReplace.length!=0){ excelEntity.setReplace(dictReplace); } } } excelEntity.setOrderNum(getCellOrder(excel.orderNum(), targetId)); excelEntity.setWrap(excel.isWrap()); excelEntity.setExportImageType(excel.imageType()); excelEntity.setSuffix(excel.suffix()); excelEntity.setDatabaseFormat(excel.databaseFormat()); excelEntity.setFormat(StringUtils.isNotEmpty(excel.exportFormat()) ? excel.exportFormat() : excel.format()); excelEntity.setStatistics(excel.isStatistics()); String fieldname = field.getName(); //update-begin-author:taoyan date:20200319 for:autopoi 双表头问题 #862 基于注解的解决方案 excelEntity.setKey(fieldname); //update-end-author:taoyan date:20200319 for:autopoi 双表头问题 #862 基于注解的解决方案 //update-begin-author:taoyan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 excelEntity.setNumFormat(excel.numFormat()); //update-end-author:taoyan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 // 动态列配置 excelEntity.setDynamic(excel.dynamic()); excelEntity.setDynamicField(excel.dynamicField()); excelEntity.setDynamicValue(excel.dynamicVal()); excelEntity.setDynamicKeepSelf(excel.dynamicKeepSelf()); //update-begin-author:liusq date:202010723 for:Excel注解的isColumnHidden方法未实现 excelEntity.setColumnHidden(excel.isColumnHidden()); //update-end-author:liusq date:202010723 for:Excel注解的isColumnHidden方法未实现 //update-begin-author:taoyan date:20180615 for:TASK #2798 【例子】导入扩展方法,支持自定义导入字段转换规则 excelEntity.setMethod(PoiPublicUtil.getMethod(fieldname, pojoClass,excel.exportConvert())); //update-end-author:taoyan date:20180615 for:TASK #2798 【例子】导入扩展方法,支持自定义导入字段转换规则 //update-begin-author:taoyan date:20180801 for:TASK #3038 【bug】Excel 导出多个值(逗号隔开的情况下,导出字典值是ID值) excelEntity.setMultiReplace(excel.multiReplace()); //update-end-author:taoyan date:20180801 for:TASK #3038 【bug】Excel 导出多个值(逗号隔开的情况下,导出字典值是ID值) //update-begin-author:taoyan date:20200319 for:autopoi 双表头问题 #862 基于实体注解的解决方案 if(StringUtils.isNotEmpty(excel.groupName())){ excelEntity.setGroupName(excel.groupName()); excelEntity.setColspan(true); } //update-end-author:taoyan date:20200319 for:autopoi 双表头问题 #862 基于实体注解的解决方案 } /** * 判断在这个单元格显示的名称 * * @param exportName * @param targetId * @return */ public String getExcelName(String exportName, String targetId) { if (exportName.indexOf(",") < 0 || targetId==null) { return exportName; } String[] arr = exportName.split(","); for (String str : arr) { if (str.indexOf(targetId) != -1) { return str.split("_")[0]; } } return null; } /** * 多个反射获取值 * * @param list * @param t * @return * @throws Exception */ public Object getFieldBySomeMethod(List list, Object t) throws Exception { for (Method m : list) { if (t == null) { t = ""; break; } t = m.invoke(t, new Object[] {}); } return t; } /** * 根据注解获取行高 * * @param excelParams * @return */ public short getRowHeight(List excelParams) { double maxHeight = 0; for (int i = 0; i < excelParams.size(); i++) { maxHeight = maxHeight > excelParams.get(i).getHeight() ? maxHeight : excelParams.get(i).getHeight(); if (excelParams.get(i).getList() != null) { for (int j = 0; j < excelParams.get(i).getList().size(); j++) { maxHeight = maxHeight > excelParams.get(i).getList().get(j).getHeight() ? maxHeight : excelParams.get(i).getList().get(j).getHeight(); } } } return (short) (maxHeight * 50); } /** * 判断字符串是否是整数 */ public boolean isInteger(String value) { try { Integer.parseInt(value); return true; } catch (NumberFormatException e) { return false; } } private Object replaceValue(String[] replace, String value) { String[] temp; for (String str : replace) { //temp = str.split("_"); {'男_sheng_1','女_2'} //update-begin-author:liusq date:20210127 for:字符串截取修改 temp = getValueArr(str); //update-end-author:liusq date:20210127 for:字符串截取修改 //update-begin---author:scott Date:20211220 for:[issues/I4MBB3]@Excel dicText字段的值有下划线时,导入功能不能正确解析--- if (value.equals(temp[1]) || value.replace("_","---").equals(temp[1])) { value = temp[0]; break; } //update-end---author:scott Date:20211220 for:[issues/I4MBB3]@Excel dicText字段的值有下划线时,导入功能不能正确解析--- } return value; } //update-begin-author:taoyan date:20180731 for:TASK #3038 【bug】Excel 导出多个值(逗号隔开的情况下,导出字典值是ID值) /** * 如果需要被替换的值是多选项,则每一项之间有逗号隔开,走以下方法 * @author taoYan * @since 2018年7月31日 */ private Object multiReplaceValue(String[] replace, String value) { if(value.indexOf(",")>0){ String[] radioVals = value.split(","); String[] temp; String result = ""; for(int i =0;i excelParams) { Collections.sort(excelParams); for (ExcelExportEntity entity : excelParams) { if (entity.getList() != null) { Collections.sort(entity.getList()); } } } /** * 循环ExcelExportEntity集合 附加配置信息
* 1.列排序
* 2.读取图片根路径设置(如果有字段是图片类型 并且存储在本地 则设置磁盘路径获取全地址导出)
* 3.多表头配置(仅限于单表 会走这个逻辑处理) */ public void reConfigExcelExportParams(List excelParams, ExportParams exportParams) { Set NameSet = new HashSet(); Map> groupAndColumnList = new HashMap>(); Map groupOrder = new HashMap<>(); int index = -99; for (ExcelExportEntity entity : excelParams) { if(entity.getOrderNum()==0){ entity.setOrderNum(index++); } if(entity.getExportImageType()==3){ entity.setImageBasePath(exportParams.getImageBasePath()); } if (entity.getList() != null) { Collections.sort(entity.getList()); //update-begin---author:chenrui ---date:20240402 for:生成代码后子表图片无法导出(流)------------ this.reConfigExcelExportParams(entity.getList(), exportParams); //update-end---author:chenrui ---date:20240402 for:生成代码后子表图片无法导出(流)------------ } String groupName = entity.getGroupName(); if(StringUtils.isNotEmpty(groupName)){ List ls = groupAndColumnList.get(groupName); if(ls==null){ ls = new ArrayList(); groupAndColumnList.put(groupName,ls); } ls.add(entity.getKey().toString()); Integer order = groupOrder.get(groupName); if(order==null || entity.getOrderNum() dataList = getDynamicListValue(entity, obj); if (dataList == null) { return ""; } String header = entity.getDynamicColumnName(); String titleField = StringUtils.defaultIfBlank(entity.getDynamicField(), "name"); String valueField = StringUtils.defaultIfBlank(entity.getDynamicValue(), "value"); for (Object item : dataList) { Object nameVal = PoiPublicUtil.getParamsValue(titleField, item); if (nameVal != null && header != null && header.equals(nameVal.toString())) { Object result = PoiPublicUtil.getParamsValue(valueField, item); return result == null ? "" : result; } } return ""; } /** * 获取动态列数据集合 * @param entity * @param obj * @return * @throws Exception */ protected Collection getDynamicListValue(ExcelExportEntity entity, Object obj) throws Exception { Object value; if (obj instanceof Map) { value = ((Map) obj).get(entity.getKey()); } else { value = entity.getMethods() != null ? getFieldBySomeMethod(entity.getMethods(), obj) : entity.getMethod().invoke(obj, new Object[] {}); } if (value == null) { return null; } if (value instanceof String) { Collection parsed = parseJsonArrayToList((String) value); if (parsed != null) { return parsed; } } if (value instanceof Collection) { return (Collection) value; } if (value.getClass().isArray()) { return Arrays.asList((Object[]) value); } return Collections.singletonList(value); } /** * 解析 JSON 数组字符串为集合(使用 JDK ScriptEngine,避免额外依赖) */ private Collection parseJsonArrayToList(String json) { if (StringUtils.isBlank(json)) { return null; } String trim = json.trim(); if (!trim.startsWith("[") || !trim.endsWith("]")) { return null; } try { List objects = JsonParser.parseJsonArrayToList(json); return objects; } catch (Exception e) { return null; } } /** * 处理动态列,根据数据动态扩充表头 * @param dataSet * @param excelParams */ protected void rebuildDynamicColumns(Collection dataSet, List excelParams) throws Exception { if (excelParams == null || excelParams.isEmpty() || dataSet == null || dataSet.isEmpty()) { return; } List result = new ArrayList(); for (ExcelExportEntity entity : excelParams) { if (!entity.isDynamic()) { result.add(entity); continue; } List dynamicList = buildDynamicEntities(entity, dataSet); if (dynamicList.isEmpty() && !entity.isDynamicKeepSelf()) { // 没有动态数据时仍然保留原列避免列缺失 result.add(entity); continue; } if (entity.isDynamicKeepSelf()) { result.add(copyKeepSelfEntity(entity)); } result.addAll(dynamicList); } excelParams.clear(); excelParams.addAll(result); } private ExcelExportEntity copyKeepSelfEntity(ExcelExportEntity source){ ExcelExportEntity target = copyDynamicBase(source); target.setDynamic(false); target.setDynamicField(null); target.setDynamicValue(null); target.setDynamicColumnName(null); target.setDynamicKeepSelf(false); target.setName(source.getName()); return target; } private List buildDynamicEntities(ExcelExportEntity entity, Collection dataSet) throws Exception { LinkedHashSet headers = new LinkedHashSet(); for (Object data : dataSet) { Collection list = getDynamicListValue(entity, data); if (list == null) { continue; } for (Object item : list) { Object header = PoiPublicUtil.getParamsValue(StringUtils.defaultIfBlank(entity.getDynamicField(), "name"), item); if (header != null && StringUtils.isNotBlank(header.toString())) { headers.add(header.toString()); } } } List dynamicEntities = new ArrayList(); for (String header : headers) { ExcelExportEntity copy = copyDynamicBase(entity); copy.setName(header); copy.setDynamicColumnName(header); copy.setOrderNum(entity.getOrderNum()); dynamicEntities.add(copy); } return dynamicEntities; } private ExcelExportEntity copyDynamicBase(ExcelExportEntity source) { ExcelExportEntity target = new ExcelExportEntity(); target.setType(source.getType()); target.setName(source.getName()); target.setKey(source.getKey()); target.setWidth(source.getWidth()); target.setHeight(source.getHeight()); target.setExportImageType(source.getExportImageType()); target.setImageBasePath(source.getImageBasePath()); target.setOrderNum(source.getOrderNum()); target.setWrap(source.isWrap()); target.setNeedMerge(source.isNeedMerge()); target.setMergeVertical(source.isMergeVertical()); target.setMergeRely(source.getMergeRely()); target.setSuffix(source.getSuffix()); target.setStatistics(source.isStatistics()); target.setColspan(source.isColspan()); target.setSubColumnList(source.getSubColumnList()); target.setGroupName(source.getGroupName()); target.setColumnHidden(source.isColumnHidden()); target.setReplace(source.getReplace()); target.setMethod(source.getMethod()); target.setMethods(source.getMethods()); target.setMultiReplace(source.isMultiReplace()); target.setNumFormat(source.getNumFormat()); target.setDatabaseFormat(source.getDatabaseFormat()); target.setFormat(source.getFormat()); target.setFixedIndex(source.getFixedIndex()); target.setDict(source.getDict()); target.setHyperlink(source.isHyperlink()); target.setMergeVertical(source.isMergeVertical()); // 动态配置 target.setDynamic(true); target.setDynamicField(source.getDynamicField()); target.setDynamicValue(source.getDynamicValue()); target.setDynamicKeepSelf(source.isDynamicKeepSelf()); return target; } /** * 字典文本中含多个下划线横岗,取最后一个(解决空值情况) * * @param val * @return */ public String[] getValueArr(String val) { int i = val.lastIndexOf("_");//最后一个分隔符的位置 String[] c=new String[2]; c[0]=val.substring(0, i); //label c[1]=val.substring(i+1); //key return c; } //update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列 } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/export/styler/AbstractExcelExportStyler.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.export.styler; import org.apache.poi.ss.usermodel.BuiltinFormats; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.jeecgframework.poi.excel.entity.params.ExcelForEachParams; /** * 抽象接口提供两个公共方法 * * @author JEECG * @date 2015年1月9日 下午5:48:55 */ public abstract class AbstractExcelExportStyler implements IExcelExportStyler { // 单行 protected CellStyle stringNoneStyle; protected CellStyle stringNoneWrapStyle; // 间隔行 protected CellStyle stringSeptailStyle; protected CellStyle stringSeptailWrapStyle; protected Workbook workbook; protected static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat("TEXT"); protected void createStyles(Workbook workbook) { this.stringNoneStyle = stringNoneStyle(workbook, false); this.stringNoneWrapStyle = stringNoneStyle(workbook, true); this.stringSeptailStyle = stringSeptailStyle(workbook, false); this.stringSeptailWrapStyle = stringSeptailStyle(workbook, true); this.workbook = workbook; } @Override public CellStyle getStyles(boolean noneStyler, ExcelExportEntity entity) { if (noneStyler && (entity == null || entity.isWrap())) { return stringNoneWrapStyle; } if (noneStyler) { return stringNoneStyle; } if (noneStyler == false && (entity == null || entity.isWrap())) { return stringSeptailWrapStyle; } return stringSeptailStyle; } public CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) { return null; } public CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) { return null; } /** * 获取模板样式(列循环时用到) * @param isSingle * @param excelForEachParams * @return */ @Override public CellStyle getTemplateStyles(boolean isSingle, ExcelForEachParams excelForEachParams) { return null; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/export/styler/ExcelExportStylerBorderImpl.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.export.styler; import org.apache.poi.ss.usermodel.*; /** * 带有边框的Excel样式 * * @author JEECG * @date 2015年1月9日 下午5:55:29 */ public class ExcelExportStylerBorderImpl extends AbstractExcelExportStyler implements IExcelExportStyler { public ExcelExportStylerBorderImpl(Workbook workbook) { super.createStyles(workbook); } @Override public CellStyle getHeaderStyle(short color) { CellStyle titleStyle = workbook.createCellStyle(); Font font = workbook.createFont(); font.setFontHeightInPoints((short) 12); titleStyle.setFont(font); titleStyle.setBorderLeft(BorderStyle.THIN); // 左边框 titleStyle.setBorderRight(BorderStyle.THIN); // 右边框 titleStyle.setBorderBottom(BorderStyle.THIN); titleStyle.setBorderTop(BorderStyle.THIN); titleStyle.setAlignment(HorizontalAlignment.CENTER); titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); return titleStyle; } @Override public CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) { CellStyle style = workbook.createCellStyle(); style.setBorderLeft(BorderStyle.THIN); // 左边框 style.setBorderRight(BorderStyle.THIN); // 右边框 style.setBorderBottom(BorderStyle.THIN); style.setBorderTop(BorderStyle.THIN); style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); style.setDataFormat(STRING_FORMAT); if (isWarp) { style.setWrapText(true); } return style; } @Override public CellStyle getTitleStyle(short color) { CellStyle titleStyle = workbook.createCellStyle(); titleStyle.setBorderLeft(BorderStyle.THIN); // 左边框 titleStyle.setBorderRight(BorderStyle.THIN); // 右边框 titleStyle.setBorderBottom(BorderStyle.THIN); titleStyle.setBorderTop(BorderStyle.THIN); titleStyle.setAlignment(HorizontalAlignment.CENTER); titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); titleStyle.setWrapText(true); return titleStyle; } @Override public CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) { return isWarp ? stringNoneWrapStyle : stringNoneStyle; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/export/styler/ExcelExportStylerColorImpl.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.export.styler; import org.apache.poi.ss.usermodel.*; /** * 带有样式的导出服务 * * @author JEECG * @date 2015年1月9日 下午4:54:15 */ public class ExcelExportStylerColorImpl extends AbstractExcelExportStyler implements IExcelExportStyler { public ExcelExportStylerColorImpl(Workbook workbook) { super.createStyles(workbook); } @Override public CellStyle getHeaderStyle(short headerColor) { CellStyle titleStyle = workbook.createCellStyle(); Font font = workbook.createFont(); font.setFontHeightInPoints((short) 24); titleStyle.setFont(font); titleStyle.setFillForegroundColor(headerColor); titleStyle.setAlignment(HorizontalAlignment.CENTER); titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); return titleStyle; } @Override public CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) { CellStyle style = workbook.createCellStyle(); style.setBorderLeft(BorderStyle.THIN); // 左边框 style.setBorderRight(BorderStyle.THIN); // 右边框 style.setBorderBottom(BorderStyle.THIN); style.setBorderTop(BorderStyle.THIN); style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); style.setDataFormat(STRING_FORMAT); if (isWarp) { style.setWrapText(true); } return style; } @Override public CellStyle getTitleStyle(short color) { CellStyle titleStyle = workbook.createCellStyle(); titleStyle.setFillForegroundColor(color); // 填充的背景颜色 titleStyle.setAlignment(HorizontalAlignment.CENTER); titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 填充图案 titleStyle.setWrapText(true); return titleStyle; } @Override public CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) { CellStyle style = workbook.createCellStyle(); style.setBorderLeft(BorderStyle.THIN); // 左边框 style.setBorderRight(BorderStyle.THIN); // 右边框 style.setBorderBottom(BorderStyle.THIN); style.setBorderTop(BorderStyle.THIN); style.setFillForegroundColor((short) 41); // 填充的背景颜色 style.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 填充图案 style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); style.setDataFormat(STRING_FORMAT); if (isWarp) { style.setWrapText(true); } return style; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/export/styler/ExcelExportStylerDefaultImpl.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.export.styler; import org.apache.poi.ss.usermodel.*; /** * 样式的默认实现 * * @author JEECG * @date 2015年1月9日 下午5:36:08 */ public class ExcelExportStylerDefaultImpl extends AbstractExcelExportStyler implements IExcelExportStyler { public ExcelExportStylerDefaultImpl(Workbook workbook) { super.createStyles(workbook); } @Override public CellStyle getTitleStyle(short color) { CellStyle titleStyle = workbook.createCellStyle(); titleStyle.setAlignment(HorizontalAlignment.CENTER); titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); titleStyle.setWrapText(true); return titleStyle; } @Override public CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) { CellStyle style = workbook.createCellStyle(); style.setAlignment( HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); style.setDataFormat(STRING_FORMAT); if (isWarp) { style.setWrapText(true); } return style; } @Override public CellStyle getHeaderStyle(short color) { CellStyle titleStyle = workbook.createCellStyle(); Font font = workbook.createFont(); font.setFontHeightInPoints((short) 12); titleStyle.setFont(font); titleStyle.setAlignment( HorizontalAlignment.CENTER); titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); return titleStyle; } @Override public CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) { CellStyle style = workbook.createCellStyle(); style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); style.setDataFormat(STRING_FORMAT); if (isWarp) { style.setWrapText(true); } return style; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/export/styler/IExcelExportStyler.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.export.styler; import org.apache.poi.ss.usermodel.CellStyle; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.jeecgframework.poi.excel.entity.params.ExcelForEachParams; /** * Excel导出样式接口 * * @author JEECG * @date 2015年1月9日 下午5:32:30 */ public interface IExcelExportStyler { /** * 列表头样式 * * @param headerColor * @return */ public CellStyle getHeaderStyle(short headerColor); /** * 标题样式 * * @param color * @return */ public CellStyle getTitleStyle(short color); /** * 获取样式方法 * * @param noneStyler * @param entity * @return */ public CellStyle getStyles(boolean noneStyler, ExcelExportEntity entity); /** * 模板使用的样式设置 */ public CellStyle getTemplateStyles(boolean isSingle, ExcelForEachParams excelForEachParams); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.export.template; import java.lang.reflect.Field; import java.util.*; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFClientAnchor; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.jeecgframework.poi.cache.ExcelCache; import org.jeecgframework.poi.cache.ImageCache; import org.jeecgframework.poi.consts.ImageScaleMode; import org.jeecgframework.poi.entity.ImageEntity; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import org.jeecgframework.poi.excel.annotation.ExcelTarget; import org.jeecgframework.poi.excel.entity.TemplateExportParams; import org.jeecgframework.poi.excel.entity.enmus.ExcelType; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.jeecgframework.poi.excel.entity.params.ExcelForEachParams; import org.jeecgframework.poi.excel.entity.params.ExcelTemplateParams; import org.jeecgframework.poi.excel.export.base.ExcelExportBase; import org.jeecgframework.poi.excel.export.styler.IExcelExportStyler; import org.jeecgframework.poi.excel.html.helper.MergedRegionHelper; import org.jeecgframework.poi.exception.excel.ExcelExportException; import org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum; import static org.jeecgframework.poi.util.PoiElUtil.*; import org.jeecgframework.poi.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Excel 导出根据模板导出 * * @author JEECG * @date 2013-10-17 * @version 1.0 */ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { private static final Logger LOGGER = LoggerFactory.getLogger(ExcelExportOfTemplateUtil.class); /** * 缓存TEMP 的for each创建的cell ,跳过这个cell的模板语法查找,提高效率 */ private Set tempCreateCellSet = new HashSet(); /** * 模板参数,全局都用到 */ private TemplateExportParams teplateParams; /** * 单元格合并信息 */ private MergedRegionHelper mergedRegionHelper; /** * 往Sheet 填充正常数据,根据表头信息 使用导入的部分逻辑,坐对象映射 * * @param teplateParams * @param pojoClass * @param dataSet * @param workbook */ private void addDataToSheet(Class pojoClass, Collection dataSet, Sheet sheet, Workbook workbook) throws Exception { if (workbook instanceof XSSFWorkbook) { super.type = ExcelType.XSSF; } // 获取表头数据 Map titlemap = getTitleMap(sheet); Drawing patriarch = sheet.createDrawingPatriarch(); // 得到所有字段 Field[] fileds = PoiPublicUtil.getClassFields(pojoClass); ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class); String targetId = null; if (etarget != null) { targetId = etarget.value(); } // 获取实体对象的导出数据 List excelParams = new ArrayList(); getAllExcelField(null, targetId, fileds, excelParams, pojoClass, null); // 根据表头进行筛选排序 sortAndFilterExportField(excelParams, titlemap); short rowHeight = getRowHeight(excelParams); int index = teplateParams.getHeadingRows() + teplateParams.getHeadingStartRow(), titleHeight = index; // 下移数据,模拟插入 sheet.shiftRows(teplateParams.getHeadingRows() + teplateParams.getHeadingStartRow(), sheet.getLastRowNum(), getShiftRows(dataSet, excelParams), true, true); if (excelParams.size() == 0) { return; } Iterator its = dataSet.iterator(); while (its.hasNext()) { Object t = its.next(); index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight); } // 合并同类项 mergeCells(sheet, excelParams, titleHeight); } /** * 下移数据 * * @param its * @param excelParams * @return */ private int getShiftRows(Collection dataSet, List excelParams) throws Exception { int size = 0; Iterator its = dataSet.iterator(); while (its.hasNext()) { Object t = its.next(); size += getOneObjectSize(t, excelParams); } return size; } /** * 获取单个对象的高度,主要是处理一堆多的情况 * * @param styles * @param rowHeight * @throws Exception */ public int getOneObjectSize(Object t, List excelParams) throws Exception { ExcelExportEntity entity; int maxHeight = 1; for (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) { entity = excelParams.get(k); if (entity.getList() != null) { Collection list = (Collection) entity.getMethod().invoke(t, new Object[] {}); if (list != null && list.size() > maxHeight) { maxHeight = list.size(); } } } return maxHeight; } public Workbook createExcleByTemplate(TemplateExportParams params, Class pojoClass, Collection dataSet, Map map) { // step 1. 判断模板的地址 if (params == null || map == null || (StringUtils.isEmpty(params.getTemplateUrl()) && params.getTemplateWb() == null)) { throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR); } Workbook wb = null; // step 2. 判断模板的Excel类型,解析模板 try { this.teplateParams = params; //update-begin-author:liusq---date:2024-09-03--for: [issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式 if (params.getTemplateWb() != null) { wb = params.getTemplateWb(); } else { wb = getCloneWorkBook(); } //update-end-author:liusq---date:2024-09-03--for: [issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式 // 创建表格样式 setExcelExportStyler((IExcelExportStyler) teplateParams.getStyle().getConstructor(Workbook.class).newInstance(wb)); // step 3. 解析模板 //update-begin---author:chenrui ---date:20240801 for:[issues/6925]xlsx模版导出图片------------ if (wb instanceof XSSFWorkbook) { super.type = ExcelType.XSSF; } //update-end---author:chenrui ---date:20240801 for:[issues/6925]xlsx模版导出图片------------ for (int i = 0, le = params.isScanAllsheet() ? wb.getNumberOfSheets() : params.getSheetNum().length; i < le; i++) { if (params.getSheetName() != null && params.getSheetName().length > i && StringUtils.isNotEmpty(params.getSheetName()[i])) { wb.setSheetName(i, params.getSheetName()[i]); } tempCreateCellSet.clear(); parseTemplate(wb.getSheetAt(i), map, params.isColForEach()); } if (dataSet != null) { // step 4. 正常的数据填充 dataHanlder = params.getDataHanlder(); if (dataHanlder != null) { needHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields()); } addDataToSheet(pojoClass, dataSet, wb.getSheetAt(params.getDataSheetNum()), wb); } } catch (Exception e) { LOGGER.error(e.getMessage(), e); return null; } return wb; } /** * 克隆excel防止操作原对象,workbook无法克隆,只能对excel进行克隆 * * @param teplateParams * @throws Exception * @Author JEECG * @date 2013-11-11 */ private Workbook getCloneWorkBook() throws Exception { //update-begin-author:wangshuai date:20200730 for:jar 包上传到服务器后 autopoi 读取不到excel模版文件 #1505 return ExcelCache.getWorkbookByTemplate(teplateParams.getTemplateUrl(), teplateParams.getSheetNum(), teplateParams.isScanAllsheet()); //update-end-author:wangshuai date:20200730 for:jar 包上传到服务器后 autopoi 读取不到excel模版文件 #1505 } /** * 获取表头数据,设置表头的序号 * * @param teplateParams * @param sheet * @return */ private Map getTitleMap(Sheet sheet) { Row row = null; Iterator cellTitle; Map titlemap = new HashMap(); for (int j = 0; j < teplateParams.getHeadingRows(); j++) { row = sheet.getRow(j + teplateParams.getHeadingStartRow()); cellTitle = row.cellIterator(); int i = row.getFirstCellNum(); while (cellTitle.hasNext()) { Cell cell = cellTitle.next(); String value = cell.getStringCellValue(); if (!StringUtils.isEmpty(value)) { titlemap.put(value, i); } i = i + 1; } } return titlemap; } private void parseTemplate(Sheet sheet, Map map, boolean colForeach) throws Exception { deleteCell(sheet, map); //update-begin-author:liusq---date:20220527--for: 模板导出列循环核心代码 --- mergedRegionHelper = new MergedRegionHelper(sheet); if (colForeach) { colForeach(sheet, map); } //update-end-author:liusq---date:20220527--for: 模板导出列循环核心代码 --- Row row = null; int index = 0; while (index <= sheet.getLastRowNum()) { row = sheet.getRow(index++); if (row == null) { continue; } for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) { if (row.getCell(i) != null && !tempCreateCellSet.contains(row.getRowNum() + "_" + row.getCell(i).getColumnIndex())) { setValueForCellByMap(row.getCell(i), map); } } } } /** * 先判断删除,省得影响效率 * * @param sheet * @param map * @throws Exception */ private void deleteCell(Sheet sheet, Map map) throws Exception { Row row = null; Cell cell = null; int index = 0; while (index <= sheet.getLastRowNum()) { row = sheet.getRow(index++); if (row == null) { continue; } for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) { cell = row.getCell(i); if (row.getCell(i) != null && (cell.getCellType() == CellType.STRING || cell.getCellType() == CellType.NUMERIC)) { cell.setCellType(CellType.STRING); String text = cell.getStringCellValue(); if (text.contains(IF_DELETE)) { if (Boolean.valueOf(eval(text.substring(text.indexOf(START_STR) + 2, text.indexOf(END_STR)).trim(), map).toString())) { PoiSheetUtility.deleteColumn(sheet, i); } cell.setCellValue(""); } } } } } /** * 给每个Cell通过解析方式set值 * * @param cell * @param map */ private void setValueForCellByMap(Cell cell, Map map) throws Exception { CellType cellType = cell.getCellType(); if (cellType != CellType.STRING && cellType != CellType.NUMERIC) { return; } String oldString; cell.setCellType(CellType.STRING); oldString = cell.getStringCellValue(); if (oldString != null && oldString.indexOf(START_STR) != -1 && !oldString.contains(FOREACH)) { // step 2. 判断是否含有解析函数 String params = null; boolean isNumber = false; if (isNumber(oldString)) { isNumber = true; oldString = oldString.replace(NUMBER_SYMBOL, ""); } while (oldString.indexOf(START_STR) != -1) { params = oldString.substring(oldString.indexOf(START_STR) + 2, oldString.indexOf(END_STR)); //update-begin-author:liusq---date:2025-06-04--for: [issues/8230] autopoi使用模板导出时,如果传入的map中存在值为null时会导致异常出错,导出失败 oldString = oldString.replace(START_STR + params + END_STR, ObjectUtils.isNotEmpty(eval(params, map))?eval(params, map).toString():""); //update-end-author:liusq---date:2025-06-04--for:[issues/8230] autopoi使用模板导出时,如果传入的map中存在值为null时会导致异常出错,导出失败 } // 如何是数值 类型,就按照数值类型进行设置 if (isNumber && StringUtils.isNotBlank(oldString)) { cell.setCellValue(Double.parseDouble(oldString)); cell.setCellType(CellType.NUMERIC); } else { cell.setCellValue(oldString); } } // 判断foreach 这种方法 if (oldString != null && oldString.contains(FOREACH)) { addListDataToExcel(cell, map, oldString.trim()); } } private boolean isNumber(String text) { return text.startsWith(NUMBER_SYMBOL) || text.contains("{" + NUMBER_SYMBOL) || text.contains(" " + NUMBER_SYMBOL); } /** * 利用foreach循环输出数据 * * @param cell * @param map * @param oldString * @throws Exception */ private void addListDataToExcel(Cell cell, Map map, String name) throws Exception { boolean isCreate = !name.contains(FOREACH_NOT_CREATE); boolean isShift = name.contains(FOREACH_AND_SHIFT); name = name.replace(FOREACH_NOT_CREATE, EMPTY).replace(FOREACH_AND_SHIFT, EMPTY).replace(FOREACH, EMPTY).replace(START_STR, EMPTY); String[] keys = name.replaceAll("\\s{1,}", " ").trim().split(" "); Collection datas = (Collection) PoiPublicUtil.getParamsValue(keys[0], map); //update-begin-author:liusq---date:20220609--for: [issues/3328]autopoi模板导出Excel功能,$fe: 遍历不好用 --- Object[] columnsInfo = getAllDataColumns(cell, name.replace(keys[0], EMPTY), mergedRegionHelper); int rowspan = (Integer) columnsInfo[0], colspan = (Integer) columnsInfo[1]; @SuppressWarnings("unchecked") List columns = (List) columnsInfo[2]; if (datas == null) { return; } Iterator its = datas.iterator(); Row row; int rowIndex = cell.getRow().getRowNum() + 1; //处理当前行 int loopSize = 0; if (its.hasNext()) { Object t = its.next(); cell.getRow().setHeight(columns.get(0).getHeight()); loopSize = setForeachRowCellValue(isCreate, cell.getRow(), cell.getColumnIndex(), t, columns, map, rowspan, colspan, mergedRegionHelper)[0]; rowIndex += rowspan - 1 + loopSize - 1; } //修复不论后面有没有数据,都应该执行的是插入操作 if (isShift && datas.size() * rowspan > 1 && cell.getRowIndex() + rowspan <= cell.getRow().getSheet().getLastRowNum()) { int lastRowNum = cell.getRow().getSheet().getLastRowNum(); int shiftRows = lastRowNum - cell.getRowIndex() - rowspan; cell.getRow().getSheet().shiftRows(cell.getRowIndex() + rowspan, lastRowNum, (datas.size() - 1) * rowspan, true, true); //update-begin-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格,第二个表格读取错误 --- mergedRegionHelper.shiftRows(cell.getSheet(), cell.getRowIndex() + rowspan, (datas.size() - 1) * rowspan, shiftRows); PoiExcelTempUtil.reset(cell.getSheet(), cell.getRowIndex() + rowspan + (datas.size() - 1) * rowspan, cell.getRow().getSheet().getLastRowNum()); //update-end-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格,第二个表格读取错误 --- } while (its.hasNext()) { Object t = its.next(); row = createRow(rowIndex, cell.getSheet(), isCreate, rowspan); row.setHeight(columns.get(0).getHeight()); loopSize = setForeachRowCellValue(isCreate, row, cell.getColumnIndex(), t, columns, map, rowspan, colspan, mergedRegionHelper)[0]; rowIndex += rowspan + loopSize - 1; } //update-end-author:liusq---date:20220609--for: [issues/3328]autopoi模板导出Excel功能,$fe: 遍历不好用 --- } private void setForEeachCellValue(boolean isCreate, Row row, int columnIndex, Object t, List columns, Map map) throws Exception { for (int i = 0, max = columnIndex + columns.size(); i < max; i++) { if (row.getCell(i) == null) row.createCell(i); } for (int i = 0, max = columns.size(); i < max; i++) { boolean isNumber = false; String tempStr = new String(columns.get(i).getName()); if (isNumber(tempStr)) { isNumber = true; tempStr = tempStr.replace(NUMBER_SYMBOL, ""); } map.put(teplateParams.getTempParams(), t); String val = eval(tempStr, map).toString(); if (isNumber && StringUtils.isNotEmpty(val)) { row.getCell(i + columnIndex).setCellValue(Double.parseDouble(val)); row.getCell(i + columnIndex).setCellType(CellType.NUMERIC); } else { row.getCell(i + columnIndex).setCellValue(val); } row.getCell(i + columnIndex).setCellStyle(columns.get(i).getCellStyle()); tempCreateCellSet.add(row.getRowNum() + "_" + (i + columnIndex)); } } /** * 获取迭代的数据的值 * * @param cell * @param name * @return */ private List getAllDataColumns(Cell cell, String name) { List columns = new ArrayList(); cell.setCellValue(""); if (name.contains(END_STR)) { columns.add(new ExcelTemplateParams(name.replace(END_STR, EMPTY).trim(), cell.getCellStyle(), cell.getRow().getHeight())); return columns; } columns.add(new ExcelTemplateParams(name.trim(), cell.getCellStyle(), cell.getRow().getHeight())); int index = cell.getColumnIndex(); //列数 int lastCellNum = cell.getRow().getLastCellNum(); Cell tempCell; while (true) { tempCell = cell.getRow().getCell(++index); //--begin--date:2020/09/18---for:增加列数判断,防止提前跳出 if (tempCell == null&&index>=lastCellNum) { break; } String cellStringString; try {// 允许为空,单表示已经完结了,因为可能被删除了 cellStringString = tempCell.getStringCellValue(); if (StringUtils.isBlank(cellStringString)&&index>=lastCellNum) { break; } } catch (Exception e) { throw new ExcelExportException("for each 当中存在空字符串,请检查模板"); } //--end--date:2020/09/18---for:增加列数判断,防止提前跳出 // 把读取过的cell 置为空 tempCell.setCellValue(""); if (cellStringString.contains(END_STR)) { columns.add(new ExcelTemplateParams(cellStringString.trim().replace(END_STR, ""), tempCell.getCellStyle(), tempCell.getRow().getHeight())); break; } else { if (cellStringString.trim().contains(teplateParams.getTempParams())) { columns.add(new ExcelTemplateParams(cellStringString.trim(), tempCell.getCellStyle(), tempCell.getRow().getHeight())); }else if(cellStringString.trim().equals(EMPTY)){ //可能是合并的单元格,允许空数据的设置 columns.add(new ExcelTemplateParams(EMPTY, tempCell.getCellStyle(), tempCell.getRow().getHeight())); } else { // 最后一行被删除了 break; } } } return columns; } /** * 对导出序列进行排序和塞选 * * @param excelParams * @param titlemap * @return */ private void sortAndFilterExportField(List excelParams, Map titlemap) { for (int i = excelParams.size() - 1; i >= 0; i--) { if (excelParams.get(i).getList() != null && excelParams.get(i).getList().size() > 0) { sortAndFilterExportField(excelParams.get(i).getList(), titlemap); if (excelParams.get(i).getList().size() == 0) { excelParams.remove(i); } else { excelParams.get(i).setOrderNum(i); } } else { if (titlemap.containsKey(excelParams.get(i).getName())) { excelParams.get(i).setOrderNum(i); } else { excelParams.remove(i); } } } sortAllParams(excelParams); } //-----------------update-begin-author:liusq---date:20220527--for: 以下方法是模板导出列循环功能新增的方法 --- /** * 先进行列的循环,因为涉及很多数据 * * @param sheet * @param map */ private void colForeach(Sheet sheet, Map map) throws Exception { Row row = null; Cell cell = null; int index = 0; while (index <= sheet.getLastRowNum()) { row = sheet.getRow(index++); if (row == null) { continue; } for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) { cell = row.getCell(i); if (row.getCell(i) != null && (cell.getCellType() == CellType.STRING || cell.getCellType() == CellType.NUMERIC)) { String text = PoiCellUtil.getCellValue(cell); if (text.contains(FOREACH_COL) || text.contains(FOREACH_COL_VALUE)) { foreachCol(cell, map, text); } } } } } /** * 循环列表 * * @param cell * @param map * @param name * @throws Exception */ private void foreachCol(Cell cell, Map map, String name) throws Exception { boolean isCreate = name.contains(FOREACH_COL_VALUE); name = name.replace(FOREACH_COL_VALUE, EMPTY).replace(FOREACH_COL, EMPTY).replace(START_STR, EMPTY); String[] keys = name.replaceAll("\\s{1,}", " ").trim().split(" "); Collection datas = (Collection) PoiPublicUtil.getParamsValue(keys[0], map); Object[] columnsInfo = getAllDataColumns(cell, name.replace(keys[0], EMPTY), mergedRegionHelper); if (datas == null) { return; } Iterator its = datas.iterator(); int rowspan = (Integer) columnsInfo[0], colspan = (Integer) columnsInfo[1]; @SuppressWarnings("unchecked") List columns = (List) columnsInfo[2]; while (its.hasNext()) { Object t = its.next(); setForeachRowCellValue(true, cell.getRow(), cell.getColumnIndex(), t, columns, map, rowspan, colspan, mergedRegionHelper); if (cell.getRow().getCell(cell.getColumnIndex() + colspan) == null) { cell.getRow().createCell(cell.getColumnIndex() + colspan); } cell = cell.getRow().getCell(cell.getColumnIndex() + colspan); } if (isCreate) { cell = cell.getRow().getCell(cell.getColumnIndex() - 1); cell.setCellValue(cell.getStringCellValue() + END_STR); } } /** * 循环迭代创建,遍历row * * @param isCreate * @param row * @param columnIndex * @param t * @param columns * @param map * @param rowspan * @param colspan * @param mergedRegionHelper * @return rowSize, cellSize * @throws Exception */ private int[] setForeachRowCellValue(boolean isCreate, Row row, int columnIndex, Object t, List columns, Map map, int rowspan, int colspan, MergedRegionHelper mergedRegionHelper) throws Exception { createRowCellSetStyle(row, columnIndex, columns, rowspan, colspan); //填写数据 ExcelForEachParams params; int loopSize = 1; int loopCi = 1; row = row.getSheet().getRow(row.getRowNum() - rowspan + 1); for (int k = 0; k < rowspan; k++) { int ci = columnIndex; row.setHeight(getMaxHeight(k, colspan, columns)); for (int i = 0; i < colspan && i < columns.size(); i++) { boolean isNumber = false; params = columns.get(colspan * k + i); tempCreateCellSet.add(row.getRowNum() + "_" + (ci)); if (params == null) { continue; } if (StringUtils.isEmpty(params.getName()) && StringUtils.isEmpty(params.getConstValue())) { row.getCell(ci).setCellStyle(params.getCellStyle()); ci = ci + params.getColspan(); continue; } String val; Object obj = null; //是不是常量 String tempStr = params.getName(); if (StringUtils.isEmpty(params.getName())) { val = params.getConstValue(); } else { if (isHasSymbol(tempStr, NUMBER_SYMBOL)) { isNumber = true; tempStr = tempStr.replaceFirst(NUMBER_SYMBOL, ""); } map.put(teplateParams.getTempParams(), t); boolean isDict = false; String dict = null; if (isHasSymbol(tempStr, DICT_HANDLER)) { isDict = true; dict = tempStr.substring(tempStr.indexOf(DICT_HANDLER) + 5).split(";")[0]; tempStr = tempStr.replaceFirst(DICT_HANDLER, ""); tempStr = tempStr.replaceFirst(dict + ";", ""); } obj = eval(tempStr, map); if (isDict && !(obj instanceof Collection)) { obj = dictHandler.toName(dict, t, tempStr, obj); } val = obj.toString(); } if (obj != null && obj instanceof Collection) { // 需要找到哪一级别是集合 ,方便后面的replace String collectName = evalFindName(tempStr, map); int[] loop = setForEachLoopRowCellValue(row, ci, (Collection) obj, columns, params, map, rowspan, colspan, mergedRegionHelper, collectName); loopSize = Math.max(loopSize, loop[0]); i += loop[1] - 1; ci = loop[2] - params.getColspan(); } else if (obj != null && obj instanceof ImageEntity) { ImageEntity img = (ImageEntity) obj; row.getCell(ci).setCellValue(""); if (img.getRowspan() > 1 || img.getColspan() > 1) { img.setHeight(0); row.getCell(ci).getSheet().addMergedRegion(new CellRangeAddress(row.getCell(ci).getRowIndex(), row.getCell(ci).getRowIndex() + img.getRowspan() - 1, row.getCell(ci).getColumnIndex(), row.getCell(ci).getColumnIndex() + img.getColspan() - 1)); } createImageCell(row.getCell(ci), img.getHeight(), img.getRowspan(), img.getColspan(), img.getUrl(), img.getData(), img.getScaleModeEnum()); } else if (isNumber && StringUtils.isNotEmpty(val)) { row.getCell(ci).setCellValue(Double.parseDouble(val)); } else { try { row.getCell(ci).setCellValue(val); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } if (params.getCellStyle() != null) { row.getCell(ci).setCellStyle(params.getCellStyle()); } //如果合并单元格,就把这个单元格的样式和之前的保持一致 setMergedRegionStyle(row, ci, params); //合并对应单元格 //update-begin-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格,第二个表格读取错误 --- boolean isNeedMerge = (params.getRowspan() != 1 || params.getColspan() != 1) && !mergedRegionHelper.isMergedRegion(row.getRowNum() + 1, ci); //update-end-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格,第二个表格读取错误 --- if (isNeedMerge) { PoiMergeCellUtil.addMergedRegion(row.getSheet(), row.getRowNum(), row.getRowNum() + params.getRowspan() - 1, ci, ci + params.getColspan() - 1); } ci = ci + params.getColspan(); } loopCi = Math.max(loopCi, ci); // 需要把需要合并的单元格合并了 --- 不是集合的栏位合并了 if (loopSize > 1) { handlerLoopMergedRegion(row, columnIndex, columns, loopSize); } row = row.getSheet().getRow(row.getRowNum() + 1); } return new int[]{loopSize, loopCi}; } /** * 图片类型的Cell */ public void createImageCell(Cell cell, double height, int rowspan, int colspan, String imagePath, byte[] data) throws Exception { createImageCell(cell, height, rowspan, colspan, imagePath, data, ImageScaleMode.STRETCH); } /** * 图片类型的Cell(带缩放功能) * * @param cell * @param height * @param rowspan * @param colspan * @param imagePath * @param data * @param scaleMode 缩放模式:0=拉伸填充, 1=等比例缩放适应, 2=不缩放(原始大小) for [issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式 * @throws Exception * @author chenrui * @date 2025/10/28 17:36 * @deprecated 使用 {@link #createImageCell(Cell, double, int, int, String, byte[], ImageScaleMode)} 代替 */ @Deprecated public void createImageCell(Cell cell, double height, int rowspan, int colspan, String imagePath, byte[] data, int scaleMode) throws Exception { createImageCell(cell, height, rowspan, colspan, imagePath, data, ImageScaleMode.valueOf(scaleMode)); } /** * 图片类型的Cell(带缩放功能,使用枚举) * * @param cell 单元格对象 * @param height 高度 * @param rowspan 行跨度 * @param colspan 列跨度 * @param imagePath 图片路径 * @param data 图片数据 * @param scaleMode 缩放模式枚举 for [issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式 * @throws Exception * @author chenrui * @date 2025/10/28 17:36 */ public void createImageCell(Cell cell, double height, int rowspan, int colspan, String imagePath, byte[] data, ImageScaleMode scaleMode) throws Exception { if (height > cell.getRow().getHeight()) { cell.getRow().setHeight((short) height); } ClientAnchor anchor; if (type.equals(ExcelType.HSSF)) { anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + colspan), cell.getRow().getRowNum() + rowspan); } else { anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + colspan), cell.getRow().getRowNum() + rowspan); } if (StringUtils.isNotEmpty(imagePath)) { data = ImageCache.getImage(imagePath); } if (data != null) { //update-begin---author:chenrui ---date:20251028 for:[issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式 ------------ // 创建图片 Picture picture = PoiExcelGraphDataUtil.getDrawingPatriarch(cell.getSheet()).createPicture(anchor, cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); // 根据缩放模式处理图片 if (scaleMode == ImageScaleMode.FIT) { // 等比例缩放适应单元格 picture.resize(); picture.resize(getImageScale(cell, rowspan, colspan, data)); } else if (scaleMode == ImageScaleMode.ORIGINAL) { // 不缩放,保持原始大小 picture.resize(); } //update-end---author:chenrui ---date:20251028 for:[issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式 ------------ } } /** * 获取图片缩放比例,确保图片等比例适应单元格大小 * for [issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式 * @param cell 单元格对象 * @param rowspan 行跨度 * @param colspan 列跨度 * @param data 图片数据 * @return * @author chenrui * @date 2025/10/28 17:38 */ private double getImageScale(Cell cell, int rowspan, int colspan, byte[] data) { try { BufferedImage image = ImageIO.read(new java.io.ByteArrayInputStream(data)); int imageWidth = image.getWidth(); int imageHeight = image.getHeight(); // 获取单元格的实际尺寸(像素) double cellWidth = 0; for (int i = 0; i < colspan; i++) { cellWidth += cell.getSheet().getColumnWidthInPixels(cell.getColumnIndex() + i); } double cellHeight = 0; for (int i = 0; i < rowspan; i++) { Row row = cell.getSheet().getRow(cell.getRowIndex() + i); if (row != null) { cellHeight += row.getHeightInPoints() * 96.0 / 72.0; } } // 如果图片或单元格尺寸无效,返回默认缩放比例 if (imageWidth <= 0 || imageHeight <= 0 || cellWidth <= 0 || cellHeight <= 0) { return 1.0; } // 计算宽度和高度的缩放比例 double widthScale = cellWidth / imageWidth; double heightScale = cellHeight / imageHeight; // 取较小的缩放比例,确保图片完全适配在单元格内(等比例缩放) double scale = Math.min(widthScale, heightScale); // 确保缩放比例不超过1.0(不放大图片,只缩小) return Math.min(scale, 1.0); } catch (Exception e) { LOGGER.warn("创建图片锚点失败: " + e.getMessage()); return 1.0; } } /** * 处理内循环 * * @param row * @param columnIndex * @param obj * @param columns * @param params * @param map * @param rowspan * @param colspan * @param mergedRegionHelper * @param collectName * @return [rowNums, columnsNums, ciIndex] * @throws Exception */ private int[] setForEachLoopRowCellValue(Row row, int columnIndex, Collection obj, List columns, ExcelForEachParams params, Map map, int rowspan, int colspan, MergedRegionHelper mergedRegionHelper, String collectName) throws Exception { //多个一起遍历 -去掉第一层 把所有的数据遍历一遍 //STEP 1拿到所有的和当前一样项目的字段 List temp = getLoopEachParams(columns, columnIndex, collectName); Iterator its = obj.iterator(); Row tempRow = row; int nums = 0; int ci = columnIndex; while (its.hasNext()) { Object data = its.next(); map.put("loop_" + columnIndex, data); int[] loopArr = setForeachRowCellValue(false, tempRow, columnIndex, data, temp, map, rowspan, colspan, mergedRegionHelper); nums += loopArr[0]; ci = Math.max(ci, loopArr[1]); map.remove("loop_" + columnIndex); tempRow = createRow(tempRow.getRowNum() + loopArr[0], row.getSheet(), false, rowspan); } for (int i = 0; i < temp.size(); i++) { temp.get(i).setName(temp.get(i).getTempName().pop()); //都是集合 temp.get(i).setCollectCell(true); } return new int[]{nums, temp.size(), ci}; } /** * 创建并返回第一个Row * * @param sheet * @param rowIndex * @param isCreate * @param rows * @return */ private Row createRow(int rowIndex, Sheet sheet, boolean isCreate, int rows) { for (int i = 0; i < rows; i++) { if (isCreate) { sheet.createRow(rowIndex++); } else if (sheet.getRow(rowIndex++) == null) { sheet.createRow(rowIndex - 1); } } return sheet.getRow(rowIndex - rows); } /** * 根据 当前是集合的信息,把后面整个集合的迭代获取出来,并替换掉集合的前缀方便后面取数 * * @param columns * @param columnIndex * @param collectName * @return */ private List getLoopEachParams(List columns, int columnIndex, String collectName) { List temp = new ArrayList<>(); for (int i = 0; i < columns.size(); i++) { //先置为不是集合 columns.get(i).setCollectCell(false); if (columns.get(i) == null || columns.get(i).getName().contains(collectName)) { temp.add(columns.get(i)); if (columns.get(i).getTempName() == null) { columns.get(i).setTempName(new Stack<>()); } columns.get(i).setCollectCell(true); columns.get(i).getTempName().push(columns.get(i).getName()); columns.get(i).setName(columns.get(i).getName().replace(collectName, "loop_" + columnIndex)); } } return temp; } /** * 设置行样式 * @param row * @param columnIndex * @param columns * @param rowspan * @param colspan */ private void createRowCellSetStyle(Row row, int columnIndex, List columns, int rowspan, int colspan) { //所有的cell创建一遍 for (int i = 0; i < rowspan; i++) { int size = columns.size(); for (int j = columnIndex, max = columnIndex + colspan; j < max; j++) { if (row.getCell(j) == null) { row.createCell(j); CellStyle style = row.getRowNum() % 2 == 0 ? getStyles(false, size <= j - columnIndex ? null : columns.get(j - columnIndex)) : getStyles(true, size <= j - columnIndex ? null : columns.get(j - columnIndex)); //返回的styler不为空时才使用,否则使用Excel设置的,更加推荐Excel设置的样式 if (style != null) { row.getCell(j).setCellStyle(style); } } } if (i < rowspan - 1) { row = row.getSheet().getRow(row.getRowNum() + 1); } } } /** * 获取CellStyle * @param isSingle * @param excelForEachParams * @return */ private CellStyle getStyles(boolean isSingle, ExcelForEachParams excelForEachParams) { return excelExportStyler.getTemplateStyles(isSingle, excelForEachParams); } /** * 获取最大高度 * @param k * @param colspan * @param columns * @return */ private short getMaxHeight(int k, int colspan, List columns) { short high = columns.get(0).getHeight(); int n = k; while (n > 0) { if (columns.get(n * colspan).getHeight() == 0) { n--; } else { high = columns.get(n * colspan).getHeight(); break; } } return high; } private boolean isHasSymbol(String text, String symbol) { return text.startsWith(symbol) || text.contains("{" + symbol) || text.contains(" " + symbol); } /** * 迭代把不是集合的数据都合并了 * * @param row * @param columnIndex * @param columns * @param loopSize */ private void handlerLoopMergedRegion(Row row, int columnIndex, List columns, int loopSize) { for (int i = 0; i < columns.size(); i++) { if (!columns.get(i).isCollectCell()) { PoiMergeCellUtil.addMergedRegion(row.getSheet(), row.getRowNum(), row.getRowNum() + loopSize - 1, columnIndex, columnIndex + columns.get(i).getColspan() - 1); } columnIndex = columnIndex + columns.get(i).getColspan(); } } /** * 设置合并单元格的样式 * * @param row * @param ci * @param params */ private void setMergedRegionStyle(Row row, int ci, ExcelForEachParams params) { //第一行数据 for (int i = 1; i < params.getColspan(); i++) { if (params.getCellStyle() != null) { row.getCell(ci + i).setCellStyle(params.getCellStyle()); } } for (int i = 1; i < params.getRowspan(); i++) { for (int j = 0; j < params.getColspan(); j++) { if (params.getCellStyle() != null) { row.getCell(ci + j).setCellStyle(params.getCellStyle()); } } } } /** * 获取迭代的数据的值 * * @param cell * @param name * @param mergedRegionHelper * @return */ private Object[] getAllDataColumns(Cell cell, String name, MergedRegionHelper mergedRegionHelper) { List columns = new ArrayList(); cell.setCellValue(""); columns.add(getExcelTemplateParams(name.replace(END_STR, EMPTY), cell, mergedRegionHelper)); int rowspan = 1, colspan = 1; if (!name.contains(END_STR)) { int index = cell.getColumnIndex(); //保存col 的开始列 int startIndex = cell.getColumnIndex(); Row row = cell.getRow(); while (index < row.getLastCellNum()) { int colSpan = columns.get(columns.size() - 1) != null ? columns.get(columns.size() - 1).getColspan() : 1; index += colSpan; for (int i = 1; i < colSpan; i++) { //添加合并的单元格,这些单元可能不是空,但是没有值,所以也需要跳过 columns.add(null); continue; } cell = row.getCell(index); //可能是合并的单元格 if (cell == null) { //读取是判断,跳过 columns.add(null); continue; } String cellStringString; try {//不允许为空 便利单元格必须有结尾和值 cellStringString = cell.getStringCellValue(); if (StringUtils.isBlank(cellStringString) && colspan + startIndex <= index) { throw new ExcelExportException("for each 当中存在空字符串,请检查模板"); } else if (StringUtils.isBlank(cellStringString) && colspan + startIndex > index) { //读取是判断,跳过,数据为空,但是不是第一次读这一列,所以可以跳过 columns.add(new ExcelForEachParams(null, cell.getCellStyle(), (short) 0)); continue; } } catch (Exception e) { throw new ExcelExportException(ExcelExportEnum.TEMPLATE_ERROR, e); } //把读取过的cell 置为空 cell.setCellValue(""); if (cellStringString.contains(END_STR)) { columns.add(getExcelTemplateParams(cellStringString.replace(END_STR, EMPTY), cell, mergedRegionHelper)); //补全缺失的cell(合并单元格后面的) int lastCellColspan = columns.get(columns.size() - 1).getColspan(); for (int i = 1; i < lastCellColspan; i++) { //添加合并的单元格,这些单元可能不是空,但是没有值,所以也需要跳过 columns.add(null); } break; } else if (cellStringString.contains(WRAP)) { columns.add(getExcelTemplateParams(cellStringString.replace(WRAP, EMPTY), cell, mergedRegionHelper)); //发现换行符,执行换行操作 colspan = index - startIndex + 1; index = startIndex - columns.get(columns.size() - 1).getColspan(); row = row.getSheet().getRow(row.getRowNum() + 1); rowspan++; } else { columns.add(getExcelTemplateParams(cellStringString.replace(WRAP, EMPTY), cell, mergedRegionHelper)); } } } colspan = 0; for (int i = 0; i < columns.size(); i++) { colspan += columns.get(i) != null ? columns.get(i).getColspan() : 0; } colspan = colspan / rowspan; return new Object[]{rowspan, colspan, columns}; } /** * 获取模板参数 * * @param name * @param cell * @param mergedRegionHelper * @return */ private ExcelForEachParams getExcelTemplateParams(String name, Cell cell, MergedRegionHelper mergedRegionHelper) { name = name.trim(); ExcelForEachParams params = new ExcelForEachParams(name, cell.getCellStyle(), cell.getRow().getHeight()); //判断是不是常量 if (name.startsWith(CONST) && name.endsWith(CONST)) { params.setName(null); params.setConstValue(name.substring(1, name.length() - 1)); } //判断是不是空 if (NULL.equals(name)) { params.setName(null); params.setConstValue(EMPTY); } //获取合并单元格的数据 if (mergedRegionHelper.isMergedRegion(cell.getRowIndex() + 1, cell.getColumnIndex())) { Integer[] colAndrow = mergedRegionHelper.getRowAndColSpan(cell.getRowIndex() + 1, cell.getColumnIndex()); params.setRowspan(colAndrow[0]); params.setColspan(colAndrow[1]); } return params; } //-----------------update-end-author:liusq---date:20220527--for: 以上方法是模板导出列循环功能新增的方法 --- } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/graph/builder/ExcelChartBuildService.java ================================================ /** * */ package org.jeecgframework.poi.excel.graph.builder; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; // POI 5.x Chart API 已重构,以下导入暂时注释 // import org.apache.poi.ss.usermodel.Chart; // import org.apache.poi.ss.usermodel.charts.AxisCrosses; // import org.apache.poi.ss.usermodel.charts.AxisPosition; // import org.apache.poi.ss.usermodel.charts.ChartAxis; // import org.apache.poi.ss.usermodel.charts.ChartDataSource; // import org.apache.poi.ss.usermodel.charts.ChartLegend; // import org.apache.poi.ss.usermodel.charts.DataSources; // import org.apache.poi.ss.usermodel.charts.LegendPosition; // import org.apache.poi.ss.usermodel.charts.LineChartData; // import org.apache.poi.ss.usermodel.charts.ScatterChartData; // import org.apache.poi.ss.usermodel.charts.ValueAxis; import org.apache.poi.ss.util.CellRangeAddress; import org.jeecgframework.poi.excel.graph.constant.ExcelGraphElementType; import org.jeecgframework.poi.excel.graph.constant.ExcelGraphType; import org.jeecgframework.poi.excel.graph.entity.ExcelGraph; import org.jeecgframework.poi.excel.graph.entity.ExcelGraphElement; import org.jeecgframework.poi.excel.graph.entity.ExcelTitleCell; import org.jeecgframework.poi.util.PoiCellUtil; import org.jeecgframework.poi.util.PoiExcelGraphDataUtil; /** * @Description Excel图表功能 * @author liusq * @data 2022年1月4号 * @deprecated Chart API 在 POI 5.x 中已被完全重构,此类暂时不支持 POI 5.x * 如需使用图表功能,请降级到 POI 4.x 或等待后续版本适配 */ @Deprecated public class ExcelChartBuildService { /** * * @param workbook * @param graphList * @param build 通过实时数据行来重新计算图形定义 * @param append * @deprecated Chart API 在 POI 5.x 中已被完全重构,暂不支持 */ @Deprecated public static void createExcelChart(Workbook workbook, List graphList, Boolean build, Boolean append) { throw new UnsupportedOperationException("Chart API 在 POI 5.x 中已被完全重构,此功能暂不支持。请降级到 POI 4.x 或等待后续版本适配。"); } /** * 构建基础图形 * @param drawing * @param anchor * @param dataSourceSheet * @param graph * @deprecated Chart API 在 POI 5.x 中已被完全重构,暂不支持 */ @Deprecated private static void buildExcelChart(Drawing drawing,ClientAnchor anchor,Sheet dataSourceSheet,ExcelGraph graph){ throw new UnsupportedOperationException("Chart API 在 POI 5.x 中已被完全重构,此功能暂不支持。"); /* POI 5.x Chart API 已重构,以下代码暂时注释 Chart chart = null; // TODO 图表没有成功 //drawing.createChart(anchor); ChartLegend legend = chart.getOrCreateLegend(); legend.setPosition(LegendPosition.TOP_RIGHT); ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); ExcelGraphElement categoryElement=graph.getCategory(); ChartDataSource categoryChart; if(categoryElement!=null&& categoryElement.getElementType().equals(ExcelGraphElementType.STRING_TYPE)){ categoryChart=DataSources.fromStringCellRange(dataSourceSheet, new CellRangeAddress(categoryElement.getStartRowNum(),categoryElement.getEndRowNum(),categoryElement.getStartColNum(),categoryElement.getEndColNum())); }else{ categoryChart=DataSources.fromNumericCellRange(dataSourceSheet, new CellRangeAddress(categoryElement.getStartRowNum(),categoryElement.getEndRowNum(),categoryElement.getStartColNum(),categoryElement.getEndColNum())); } List valueList=graph.getValueList(); List> chartValueList= new ArrayList<>(); if(valueList!=null&&valueList.size()>0){ for(ExcelGraphElement ele:valueList){ ChartDataSource source=DataSources.fromNumericCellRange(dataSourceSheet, new CellRangeAddress(ele.getStartRowNum(),ele.getEndRowNum(),ele.getStartColNum(),ele.getEndColNum())); chartValueList.add(source); } } if(graph.getGraphType().equals(ExcelGraphType.LINE_CHART)){ LineChartData data = chart.getChartDataFactory().createLineChartData(); buildLineChartData(data, categoryChart, chartValueList, graph.getTitle()); chart.plot(data, bottomAxis, leftAxis); } else { ScatterChartData data=chart.getChartDataFactory().createScatterChartData(); buildScatterChartData(data, categoryChart, chartValueList,graph.getTitle()); chart.plot(data, bottomAxis, leftAxis); } */ } /** * 构建多个图形对象 * @param dataSourceSheet * @param tragetSheet * @param graphList */ private static void buildExcelChart(Sheet dataSourceSheet,Sheet tragetSheet,List graphList){ int len=graphList.size(); if(len==1) { buildExcelChart(dataSourceSheet, tragetSheet, graphList.get(0)); } else { int drawStart=0; int drawEnd=20; Drawing drawing = PoiExcelGraphDataUtil.getDrawingPatriarch(tragetSheet); for(int i=0;i0){ }else{ for(int i=0;i graphList){ if(graphList!=null&&graphList.size()>0){ for(ExcelGraph graph:graphList){ if(graph!=null) { buildTitle(sheet, graph); } } } } /** * * @param data * @param categoryChart * @param chartValueList * @param title * @deprecated Chart API 在 POI 5.x 中已被完全重构,暂不支持 */ @Deprecated private static void buildLineChartData(Object data, Object categoryChart, List chartValueList, List title){ throw new UnsupportedOperationException("Chart API 在 POI 5.x 中已被完全重构,此功能暂不支持。"); /* POI 5.x Chart API 已重构,以下代码暂时注释 if(chartValueList.size()==title.size()) { int len=title.size(); for(int i=0;i source:chartValueList){ String temp_title=title.get(i); if(StringUtils.isNotBlank(temp_title)){ //data.addSerie(categoryChart, source).setTitle(_title); }else{ //data.addSerie(categoryChart, source); } } } */ } /** * * @param data * @param categoryChart * @param chartValueList * @param title * @deprecated Chart API 在 POI 5.x 中已被完全重构,暂不支持 */ @Deprecated private static void buildScatterChartData(Object data, Object categoryChart, List chartValueList, List title){ throw new UnsupportedOperationException("Chart API 在 POI 5.x 中已被完全重构,此功能暂不支持。"); /* POI 5.x Chart API 已重构,以下代码暂时注释 if(chartValueList.size()==title.size()) { int len=title.size(); for(int i=0;i source:chartValueList){ String temp_title=title.get(i); if(StringUtils.isNotBlank(temp_title)){ data.addSerie(categoryChart, source).setTitle(temp_title); }else{ data.addSerie(categoryChart, source); } } } */ } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/graph/constant/ExcelGraphElementType.java ================================================ /** * */ package org.jeecgframework.poi.excel.graph.constant; /** * @Description 定义元素类型 * @author liusq * @date 2022年1月4号 */ public interface ExcelGraphElementType { public static final Integer STRING_TYPE =1; public static final Integer NUMERIC_TYPE =2; } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/graph/constant/ExcelGraphType.java ================================================ /** * */ package org.jeecgframework.poi.excel.graph.constant; /** * @Description 定义图形类型 * @author liusq * @date 2022年1月4号 */ public interface ExcelGraphType { public static final Integer LINE_CHART =1; public static final Integer SCATTER_CHART =2; } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/graph/entity/ExcelGraph.java ================================================ /** * */ package org.jeecgframework.poi.excel.graph.entity; import java.util.List; /** * @Description Excel 图形构造服务 * @author liusq * @date 2022年1月4号 */ public interface ExcelGraph { public ExcelGraphElement getCategory(); public List getValueList(); public Integer getGraphType(); public List getTitleCell(); public List getTitle(); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/graph/entity/ExcelGraphDefined.java ================================================ /** * */ package org.jeecgframework.poi.excel.graph.entity; import org.jeecgframework.poi.excel.graph.constant.ExcelGraphType; import java.util.ArrayList; import java.util.List; /** * @Description Excel 图形构造服务 * @author liusq * @date 2022年1月4号 */ public class ExcelGraphDefined implements ExcelGraph { private ExcelGraphElement category; public List valueList= new ArrayList<>(); public List titleCell= new ArrayList<>(); private Integer graphType= ExcelGraphType.LINE_CHART; public List title= new ArrayList<>(); @Override public ExcelGraphElement getCategory() { return category; } public void setCategory(ExcelGraphElement category) { this.category = category; } @Override public List getValueList() { return valueList; } public void setValueList(List valueList) { this.valueList = valueList; } @Override public Integer getGraphType() { return graphType; } public void setGraphType(Integer graphType) { this.graphType = graphType; } @Override public List getTitleCell() { return titleCell; } public void setTitleCell(List titleCell) { this.titleCell = titleCell; } @Override public List getTitle() { return title; } public void setTitle(List title) { this.title = title; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/graph/entity/ExcelGraphElement.java ================================================ /** * */ package org.jeecgframework.poi.excel.graph.entity; import org.jeecgframework.poi.excel.graph.constant.ExcelGraphElementType; /** * @Description Excel 图形构造服务 * @author liusq * @date 2022年1月4号 */ public class ExcelGraphElement { private Integer startRowNum; private Integer endRowNum; private Integer startColNum; private Integer endColNum; private Integer elementType= ExcelGraphElementType.STRING_TYPE; public Integer getStartRowNum() { return startRowNum; } public void setStartRowNum(Integer startRowNum) { this.startRowNum = startRowNum; } public Integer getEndRowNum() { return endRowNum; } public void setEndRowNum(Integer endRowNum) { this.endRowNum = endRowNum; } public Integer getStartColNum() { return startColNum; } public void setStartColNum(Integer startColNum) { this.startColNum = startColNum; } public Integer getEndColNum() { return endColNum; } public void setEndColNum(Integer endColNum) { this.endColNum = endColNum; } public Integer getElementType() { return elementType; } public void setElementType(Integer elementType) { this.elementType = elementType; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/graph/entity/ExcelTitleCell.java ================================================ /** * */ package org.jeecgframework.poi.excel.graph.entity; /** * @Description Excel 图形构造服务 * @author liusq * @date 2022年1月4号 */ public class ExcelTitleCell { private Integer row; private Integer col; public ExcelTitleCell(){ } public ExcelTitleCell(Integer row,Integer col){ this.row=row; this.col=col; } public Integer getRow() { return row; } public void setRow(Integer row) { this.row = row; } public Integer getCol() { return col; } public void setCol(Integer col) { this.col = col; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/graph/package-info.java ================================================ /** * */ /** * @Description Excel 图形构造服务 * @author liusq * @date 2022年1月4号 */ package org.jeecgframework.poi.excel.graph; ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/html/ExcelToHtmlServer.java ================================================ package org.jeecgframework.poi.excel.html; import java.util.Formatter; import java.util.Iterator; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.html.helper.CellValueHelper; import org.jeecgframework.poi.excel.html.helper.MergedRegionHelper; import org.jeecgframework.poi.excel.html.helper.StylerHelper; /** * Excel转换成Html 服务 * * @author JEECG * @date 2015年5月10日 上午11:41:15 */ public class ExcelToHtmlServer { private Workbook wb; private int sheetNum; private int cssRandom; /* 是不是完成界面 */ private boolean completeHTML; private Formatter out; /* 已经完成范围处理 */ private boolean gotBounds; private int firstColumn; private int endColumn; private static final String COL_HEAD_CLASS = "colHeader"; // private static final String ROW_HEAD_CLASS = "rowHeader"; private static final String DEFAULTS_CLASS = "excelDefaults"; public ExcelToHtmlServer(Workbook wb, boolean completeHTML, int sheetNum) { this.wb = wb; this.completeHTML = completeHTML; this.sheetNum = sheetNum; cssRandom = (int) Math.ceil(Math.random() * 1000); } public String printPage() { try { ensureOut(); if (completeHTML) { out.format("%n"); out.format("%n"); out.format("%n"); out.format("%n"); } new StylerHelper(wb, out, sheetNum, cssRandom); if (completeHTML) { out.format("%n"); out.format("%n"); } print(); if (completeHTML) { out.format("%n"); out.format("%n"); } return out.toString(); } finally { if (out != null) out.close(); } } private void print() { printSheets(); } private void ensureOut() { if (out == null) out = new Formatter(new StringBuilder()); } private void printSheets() { Sheet sheet = wb.getSheetAt(sheetNum); printSheet(sheet); } private void printSheet(Sheet sheet) { out.format("%n", DEFAULTS_CLASS, getTableWidth(sheet)); printCols(sheet); printSheetContent(sheet); out.format("
%n"); } private void printCols(Sheet sheet) { // out.format("%n"); ensureColumnBounds(sheet); for (int i = firstColumn; i < endColumn; i++) { out.format("%n", sheet.getColumnWidth(i) / 32); } } private int getTableWidth(Sheet sheet) { ensureColumnBounds(sheet); int width = 0; for (int i = firstColumn; i < endColumn; i++) { width = width + (sheet.getColumnWidth(i) / 32); } return width; } private void ensureColumnBounds(Sheet sheet) { if (gotBounds) return; Iterator iter = sheet.rowIterator(); firstColumn = (iter.hasNext() ? Integer.MAX_VALUE : 0); endColumn = 0; while (iter.hasNext()) { Row row = iter.next(); short firstCell = row.getFirstCellNum(); if (firstCell >= 0) { firstColumn = Math.min(firstColumn, firstCell); endColumn = Math.max(endColumn, row.getLastCellNum()); } } gotBounds = true; } @SuppressWarnings("unused") /**本来是用来生成 A,B 那个列名称的**/ private void printColumnHeads(Sheet sheet) { out.format("%n"); out.format(" %n", COL_HEAD_CLASS); out.format(" ◊%n", COL_HEAD_CLASS); StringBuilder colName = new StringBuilder(); for (int i = firstColumn; i < endColumn; i++) { colName.setLength(0); int cnum = i; do { colName.insert(0, (char) ('A' + cnum % 26)); cnum /= 26; } while (cnum > 0); out.format(" %s%n", COL_HEAD_CLASS, colName); } out.format(" %n"); out.format("%n"); } private void printSheetContent(Sheet sheet) { // printColumnHeads(sheet); MergedRegionHelper mergedRegionHelper = new MergedRegionHelper(sheet); CellValueHelper cellValueHelper = new CellValueHelper(wb, cssRandom); out.format("%n"); Iterator rows = sheet.rowIterator(); int rowIndex = 1; while (rows.hasNext()) { Row row = rows.next(); out.format(" %n", row.getHeight() / 15); // out.format(" %d%n", ROW_HEAD_CLASS, // row.getRowNum() + 1); for (int i = firstColumn; i < endColumn; i++) { if (mergedRegionHelper.isNeedCreate(rowIndex, i)) { String content = " "; CellStyle style = null; if (i >= row.getFirstCellNum() && i < row.getLastCellNum()) { Cell cell = row.getCell(i); if (cell != null) { style = cell.getCellStyle(); content = cellValueHelper.getHtmlValue(cell); } } if (mergedRegionHelper.isMergedRegion(rowIndex, i)) { Integer[] rowAndColSpan = mergedRegionHelper.getRowAndColSpan(rowIndex, i); out.format(" %s%n", rowAndColSpan[0], rowAndColSpan[1], styleName(style), content); } else { out.format(" %s%n", styleName(style), content); } } } out.format(" %n"); rowIndex++; } out.format("%n"); } private String styleName(CellStyle style) { if (style == null) return ""; return String.format("style_%02x_%s font_%s_%s", style.getIndex(), cssRandom, style.getFontIndex(), cssRandom); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/CellValueHelper.java ================================================ package org.jeecgframework.poi.excel.html.helper; import java.util.HashMap; import java.util.Map; import org.apache.poi.hssf.usermodel.HSSFRichTextString; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFFont; import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import com.google.common.xml.XmlEscapers; /** * Cell值帮助类 * * @author JEECG * @date 2015年5月9日 下午10:31:32 */ public class CellValueHelper { /** * Excel 格式 */ private boolean is07; private int cssRandom; private Map fontCache = new HashMap(); public CellValueHelper(Workbook wb, int cssRandom) { this.cssRandom = cssRandom; if (wb instanceof HSSFWorkbook) is07 = false; else if (wb instanceof XSSFWorkbook) { is07 = true; cacheFontInfo(wb); } else throw new IllegalArgumentException("unknown workbook type: " + wb.getClass().getSimpleName()); } /** * O7 版本坑爹bug * * @param wb */ private void cacheFontInfo(Workbook wb) { for (int i = 0, le = wb.getNumberOfFonts(); i < le; i++) { Font font = wb.getFontAt(i); fontCache.put(font.getBold() + "_" + font.getItalic() + "_" + font.getFontName() + "_" + font.getFontHeightInPoints() + "_" + font.getColor(), font.getIndex() + ""); } } public String getHtmlValue(Cell cell) { if (CellType.BOOLEAN == cell.getCellType() || CellType.NUMERIC == cell.getCellType()) { cell.setCellType( CellType.STRING); return cell.getStringCellValue(); } else if ( CellType.STRING == cell.getCellType()) { if (cell.getRichStringCellValue().numFormattingRuns() == 0) { return XmlEscapers.xmlContentEscaper().escape(cell.getStringCellValue()); } else if (is07) { return getXSSFRichString((XSSFRichTextString) cell.getRichStringCellValue()); } else { return getHSSFRichString((HSSFRichTextString) cell.getRichStringCellValue()); } } return ""; } /** * 03版本复杂数据 * * @param rich * @return */ private String getHSSFRichString(HSSFRichTextString rich) { int nums = rich.numFormattingRuns(); StringBuilder sb = new StringBuilder(); String text = rich.toString(); int currentIndex = 0; sb.append(text.substring(0, rich.getIndexOfFormattingRun(0))); for (int i = 0; i < nums; i++) { sb.append(""); currentIndex = rich.getIndexOfFormattingRun(i); if (i < nums - 1) { sb.append(XmlEscapers.xmlContentEscaper().escape(text.substring(currentIndex, rich.getIndexOfFormattingRun(i + 1)))); } else { sb.append(XmlEscapers.xmlContentEscaper().escape(text.substring(currentIndex, text.length()))); } sb.append(""); } return sb.toString(); } /** * 07版本复杂数据 * * @param rich * @return */ private String getXSSFRichString(XSSFRichTextString rich) { int nums = rich.numFormattingRuns(); StringBuilder sb = new StringBuilder(); String text = rich.toString(); int currentIndex = 0, lastIndex = 0; for (int i = 1; i <= nums; i++) { sb.append(""); currentIndex = rich.getIndexOfFormattingRun(i) == -1 ? text.length() : rich.getIndexOfFormattingRun(i); sb.append(XmlEscapers.xmlContentEscaper().escape(text.substring(lastIndex, currentIndex))); sb.append(""); lastIndex = currentIndex; } return sb.toString(); } private String getFontIndex(XSSFFont font) { return fontCache.get(font.getBold() + "_" + font.getItalic() + "_" + font.getFontName() + "_" + font.getFontHeightInPoints() + "_" + font.getColor()); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/MergedRegionHelper.java ================================================ package org.jeecgframework.poi.excel.html.helper; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.poi.ss.usermodel.Sheet; import org.jeecgframework.poi.util.PoiCellUtil; import org.jeecgframework.poi.util.PoiMergeCellUtil; /** * 合并单元格帮助类 * * @author JEECG * @date 2015年5月9日 下午2:13:35 */ public class MergedRegionHelper { private Map mergedCache = new HashMap(); private Set notNeedCread = new HashSet(); public MergedRegionHelper(Sheet sheet) { getAllMergedRegion(sheet); } private void getAllMergedRegion(Sheet sheet) { int nums = sheet.getNumMergedRegions(); for (int i = 0; i < nums; i++) { handerMergedString(sheet.getMergedRegion(i).formatAsString()); } } /** * 根据合并输出内容,处理合并单元格事情 * * @param formatAsString */ private void handerMergedString(String formatAsString) { String[] strArr = formatAsString.split(":"); if (strArr.length == 2) { int startCol = strArr[0].charAt(0) - 65; if (strArr[0].charAt(1) >= 65) { startCol = (startCol + 1) * 26 + (strArr[0].charAt(1) - 65); } int startRol = Integer.valueOf(strArr[0].substring(strArr[0].charAt(1) >= 65 ? 2 : 1)); int endCol = strArr[1].charAt(0) - 65; if (strArr[1].charAt(1) >= 65) { endCol = (endCol + 1) * 26 + (strArr[1].charAt(1) - 65); } int endRol = Integer.valueOf(strArr[1].substring(strArr[1].charAt(1) >= 65 ? 2 : 1)); mergedCache.put(startRol + "_" + startCol, new Integer[] { endRol - startRol + 1, endCol - startCol + 1 }); for (int i = startRol; i <= endRol; i++) { for (int j = startCol; j <= endCol; j++) { notNeedCread.add(i + "_" + j); } } notNeedCread.remove(startRol + "_" + startCol); } } /** * 是不是需要创建这个TD * * @param row * @param col * @return */ public boolean isNeedCreate(int row, int col) { return !notNeedCread.contains(row + "_" + col); } /** * 是不是合并区域 * * @param row * @param col * @return */ public boolean isMergedRegion(int row, int col) { return mergedCache.containsKey(row + "_" + col); } /** * 获取合并区域 * * @param row * @param col * @return */ public Integer[] getRowAndColSpan(int row, int col) { return mergedCache.get(row + "_" + col); } /** * 插入之后还原之前的合并单元格 * * @param rowIndex * @param size */ public void shiftRows(Sheet sheet, int rowIndex, int size, int shiftRows) { Set keys = new HashSet(); keys.addAll(mergedCache.keySet()); for (String key : keys) { String[] temp = key.split("_"); //update-begin---author:chenrui ---date:20240102 for:[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题------------ if (Integer.parseInt(temp[0]) > rowIndex) { //update-end---author:chenrui ---date:20240102 for:[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题------------ Integer[] data = mergedCache.get(key); String newKey = (Integer.parseInt(temp[0]) + size) + "_" + temp[1]; if (!mergedCache.containsKey(newKey)) { mergedCache.put(newKey, mergedCache.get(key)); try { // 还原合并单元格 if (!PoiCellUtil.isMergedRegion(sheet, Integer.parseInt(temp[0]) + size - 1, Integer.parseInt(temp[1]))) { PoiMergeCellUtil.addMergedRegion(sheet, Integer.parseInt(temp[0]) + size - 1, Integer.parseInt(temp[0]) + data[0] + size - 2, Integer.parseInt(temp[1]), Integer.parseInt(temp[1]) + data[1] - 1 ); } } catch (Exception e) { } } } } //删除掉原始的缓存KEY for (String key : keys) { String[] temp = key.split("_"); //update-begin---author:chenrui ---date:20240102 for:[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题------------ if (Integer.parseInt(temp[0]) >= rowIndex && Integer.parseInt(temp[0]) <= rowIndex + size ) { //update-end---author:chenrui ---date:20240102 for:[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题------------ mergedCache.remove(key); } } } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/StylerHelper.java ================================================ package org.jeecgframework.poi.excel.html.helper; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Formatter; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.usermodel.HSSFPalette; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFColor; import org.apache.poi.xssf.usermodel.XSSFFont; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.jeecgframework.poi.util.PoiPublicUtil; /** * 样式帮助类 * * @author JEECG * @date 2015年5月9日 下午4:04:24 */ public class StylerHelper { private static String DEFAULTS_CLASS_CSS = ".excelDefaults {background-color: white;color: black;text-decoration: none;direction: ltr;text-transform: none;text-indent: 0;letter-spacing: 0;word-spacing: 0;white-space: normal;unicode-bidi: normal;vertical-align: 0;text-shadow: none;padding: 0;margin: 0;border-collapse: collapse;white-space: pre-wrap;word-wrap: break-word;word-break: break-all;}.excelDefaults td {padding: 1px 5px;border: 1px solid silver;border-color: #000000;text-align: center;vertical-align: middle;font-size: 12pt;}.excelDefaults .colHeader {background-color: silver;font-weight: bold;border: 1px solid black;text-align: center;padding: 1px 5px;}.excelDefaults .rowHeader {background-color: silver;font-weight: bold;border: 1px solid black;text-align: right;padding: 1px 5px;}"; private static final String DEFAULTS_CLASS = "excelDefaults"; private static final Map ALIGN = PoiPublicUtil.mapFor(HorizontalAlignment.LEFT.getCode(), "left",HorizontalAlignment.CENTER.getCode(), "center",HorizontalAlignment.RIGHT.getCode(), "right", HorizontalAlignment.FILL.getCode(), "left",HorizontalAlignment.JUSTIFY.getCode(), "left",HorizontalAlignment.CENTER_SELECTION.getCode(), "center"); private static final Map VERTICAL_ALIGN = PoiPublicUtil.mapFor(VerticalAlignment.BOTTOM.getCode(), "bottom", VerticalAlignment.CENTER.getCode(), "middle",VerticalAlignment.TOP.getCode(), "top"); private Formatter out; private Sheet sheet; private HtmlHelper helper; private int sheetNum; private int cssRandom; public StylerHelper(Workbook wb, Formatter out, int sheetNum, int cssRandom) { this.out = out; this.sheetNum = sheetNum; this.cssRandom = cssRandom; if (wb instanceof HSSFWorkbook) helper = new HSSFHtmlHelper((HSSFWorkbook) wb); else if (wb instanceof XSSFWorkbook) helper = new XSSFHtmlHelper((XSSFWorkbook) wb); else throw new IllegalArgumentException("unknown workbook type: " + wb.getClass().getSimpleName()); printInlineStyle(wb); } private void printInlineStyle(Workbook wb) { out.format("%n"); } private void prontFonts(Workbook wb) { //update-begin---author:liusq Date:20220228 for:[I4I3ZY]issue AutoPOi Workbook对象转HTML字符串 数组下标越界异常---- for (int i = 0, le = wb.getNumberOfFonts(); i < le; i++) { Font font = wb.getFontAt(i); out.format(".%s .%s {%n", DEFAULTS_CLASS, "font_" + i + "_" + cssRandom); fontStyle(font); out.format("}%n"); } //update-end---author:liusq Date:20220228 for:[I4I3ZY]issue AutoPOi Workbook对象转HTML字符串 数组下标越界异常整---- } public void printStyles(Workbook wb) { if (DEFAULTS_CLASS_CSS == null) { DEFAULTS_CLASS_CSS = getDefaultsClassCss(); } out.format(DEFAULTS_CLASS_CSS); Set seen = new HashSet(); sheet = wb.getSheetAt(sheetNum); Iterator rows = sheet.rowIterator(); while (rows.hasNext()) { Row row = rows.next(); for (Cell cell : row) { CellStyle style = cell.getCellStyle(); if (!seen.contains(style)) { printStyle(style); seen.add(style); } } } } private String getDefaultsClassCss() { BufferedReader in = null; StringBuilder sb = new StringBuilder(); Formatter formatter = new Formatter(sb); try { in = new BufferedReader(new InputStreamReader(StylerHelper.class.getResourceAsStream("excelStyle.css"))); String line; while ((line = in.readLine()) != null) { formatter.format("%s%n", line); } return formatter.toString(); } catch (IOException e) { throw new IllegalStateException("Reading standard css", e); } finally { if (in != null) { try { in.close(); } catch (IOException e) { throw new IllegalStateException("Reading standard css", e); } } formatter.close(); } } private void printStyle(CellStyle style) { out.format(".%s .%s {%n", DEFAULTS_CLASS, styleName(style)); styleContents(style); out.format("}%n"); } private void styleContents(CellStyle style) { if (style.getAlignment().getCode() != 2) { styleOut("text-align", style.getAlignment().getCode(), ALIGN); styleOut("vertical-align", style.getAlignment().getCode(), VERTICAL_ALIGN); } helper.colorStyles(style, out); } private void fontStyle(Font font) { if (font.getBold()) out.format(" font-weight: bold;%n"); if (font.getItalic()) out.format(" font-style: italic;%n"); out.format(" font-family: %s;%n", font.getFontName()); int fontheight = font.getFontHeightInPoints(); if (fontheight == 9) { fontheight = 10; } out.format(" font-size: %dpt;%n", fontheight); helper.styleColor(out, "color", getColor(font)); } private Color getColor(Font font) { if (helper instanceof HSSFHtmlHelper) { return ((HSSFWorkbook) sheet.getWorkbook()).getCustomPalette().getColor(font.getColor()); } else { return ((XSSFFont) font).getXSSFColor(); } } private String styleName(CellStyle style) { if (style == null) return ""; return String.format("style_%02x_%s", style.getIndex(), cssRandom); } private void styleOut(String attr, K key, Map mapping) { String value = mapping.get(key); if (value != null) { out.format(" %s: %s;%n", attr, value); } } private interface HtmlHelper { /** * Outputs the appropriate CSS style for the given cell style. * * @param style * The cell style. * @param out * The place to write the output. */ void colorStyles(CellStyle style, Formatter out); void styleColor(Formatter out, String attr, Color color); } private class HSSFHtmlHelper implements HtmlHelper { private final HSSFWorkbook wb; private final HSSFPalette colors; //-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】-------- private HSSFColor HSSF_AUTO = new HSSFColor(0x40, -1, java.awt.Color.black); //-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】-------- public HSSFHtmlHelper(HSSFWorkbook wb) { this.wb = wb; colors = wb.getCustomPalette(); } public void colorStyles(CellStyle style, Formatter out) { HSSFCellStyle cs = (HSSFCellStyle) style; if (cs.getFillPattern() != FillPatternType.NO_FILL) { out.format(" /* fill pattern = %s */%n", cs.getFillPattern()); } styleColor(out, "background-color", cs.getFillForegroundColor()); styleColor(out, "color", colors.getColor(cs.getFont(wb).getColor())); } private void styleColor(Formatter out, String attr, short index) { HSSFColor color = colors.getColor(index); if (index == HSSF_AUTO.getIndex() || color == null) { out.format(" /* %s: index = %d */%n", attr, index); } else { short[] rgb = color.getTriplet(); out.format(" %s: #%02x%02x%02x; /* index = %d */%n", attr, rgb[0], rgb[1], rgb[2], index); } } public void styleColor(Formatter out, String attr, Color color) { if (color == null) { return; } HSSFColor hSSFColor = (HSSFColor) color; short[] rgb = hSSFColor.getTriplet(); out.format(" %s: #%02x%02x%02x; %n", attr, rgb[0], rgb[1], rgb[2]); } } /** * Implementation of {@link HtmlHelper} for XSSF files. * * @author Ken Arnold, Industrious Media LLC */ private class XSSFHtmlHelper implements HtmlHelper { public XSSFHtmlHelper(XSSFWorkbook wb) { } public void colorStyles(CellStyle style, Formatter out) { XSSFCellStyle cs = (XSSFCellStyle) style; styleColor(out, "background-color", cs.getFillForegroundXSSFColor()); styleColor(out, "color", cs.getFont().getXSSFColor()); } public void styleColor(Formatter out, String attr, Color color) { XSSFColor xSSFColor = (XSSFColor) color; if (color == null || xSSFColor.isAuto()) return; byte[] rgb = xSSFColor.getRGB(); if (rgb == null) { return; } out.format(" %s: #%02x%02x%02x;%n", attr, rgb[0], rgb[1], rgb[2]); } } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/excelStyle.css ================================================ .excelDefaults { background-color: white; color: black; text-decoration: none; direction: ltr; text-transform: none; text-indent: 0; letter-spacing: 0; word-spacing: 0; white-space: normal; unicode-bidi: normal; vertical-align: 0; text-shadow: none; padding: 0; margin: 0; border-collapse: collapse; white-space: pre-wrap; word-wrap: break-word; word-break: break-all; } .excelDefaults td { padding: 1px 5px; border: 1px solid silver; border-color: #000000; text-align: center; vertical-align: middle; font-size: 12pt; } .excelDefaults .colHeader { background-color: silver; font-weight: bold; border: 1px solid black; text-align: center; padding: 1px 5px; } .excelDefaults .rowHeader { background-color: silver; font-weight: bold; border: 1px solid black; text-align: right; padding: 1px 5px; } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/CellValueServer.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.imports; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.jeecgframework.poi.excel.entity.params.ExcelImportEntity; import org.jeecgframework.poi.excel.entity.sax.SaxReadCellEntity; import org.jeecgframework.poi.exception.excel.ExcelImportException; import org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum; import org.jeecgframework.poi.handler.inter.IExcelDataHandler; import org.jeecgframework.poi.util.ExcelUtil; import org.jeecgframework.poi.util.PoiPublicUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.RoundingMode; import java.sql.Time; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.ZoneId; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; /** * Cell 取值服务 判断类型处理数据 1.判断Excel中的类型 2.根据replace替换值 3.handler处理数据 4.判断返回类型转化数据返回 * * @author JEECG * @date 2014年6月26日 下午10:42:28 */ public class CellValueServer { private static final Logger LOGGER = LoggerFactory.getLogger(CellValueServer.class); private List hanlderList = null; /** * 获取单元格内的值 * * @param xclass * @param cell * @param entity * @return */ private Object getCellValue(String xclass, Cell cell, ExcelImportEntity entity) { if (cell == null) { return ""; } Object result = null; // 日期格式比较特殊,和cell格式不一致 if ("class java.util.Date".equals(xclass) || ("class java.sql.Time").equals(xclass) || ("class java.time.LocalDate").equals(xclass) || ("class java.time.LocalDateTime").equals(xclass)) { if ( CellType.NUMERIC == cell.getCellType()) { // 日期格式 result = cell.getDateCellValue(); } else { cell.setCellType( CellType.STRING); result = getDateData(entity, cell.getStringCellValue()); } if (("class java.sql.Time").equals(xclass)) { result = new Time(((Date) result).getTime()); }else if (("class java.time.LocalDate").equals(xclass)) { result = ((Date) result).toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); } else if (("class java.time.LocalDateTime").equals(xclass)) { result = ((Date) result).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); } } else if ( CellType.NUMERIC == cell.getCellType()) { result = cell.getNumericCellValue(); } else if ( CellType.BOOLEAN == cell.getCellType()) { result = cell.getBooleanCellValue(); } else if ( CellType.FORMULA == cell.getCellType() && PoiPublicUtil.isNumber(xclass)) { //如果单元格是表达式 且 字段是数字类型 double cellValue = cell.getNumericCellValue(); //---author:liusq---date:20221102-----for: [issues/3369]Excel导入 带公式的时候精度丢失--- //setScale方法的第一个参数设置小数点保留位数,第二个参数设置进位方法、此处是四舍五入 BigDecimal bigDecimal= new BigDecimal(cellValue).setScale(4, RoundingMode.HALF_UP); //stripTrailingZeros方法去除末尾的0,toPlainString避免输出科学计数法的字符串 result = bigDecimal.stripTrailingZeros().toPlainString(); //---author:liusq---date:20221102-----for:[issues/3369] Excel导入 带公式的时候精度丢失--- } else { //设置单元格类型 cell.setCellType(CellType.STRING); result = cell.getStringCellValue(); } return result; } /** * 获取日期类型数据 * * @Author JEECG * @date 2013年11月26日 * @param entity * @param value * @return */ private Date getDateData(ExcelImportEntity entity, String value) { if (StringUtils.isNotEmpty(entity.getFormat()) && StringUtils.isNotEmpty(value)) { SimpleDateFormat format = new SimpleDateFormat(entity.getFormat()); try { return format.parse(value); } catch (ParseException e) { LOGGER.error("时间格式化失败,格式化:{},值:{}", entity.getFormat(), value); throw new ExcelImportException(ExcelImportEnum.GET_VALUE_ERROR); } } return null; } /** * 获取cell的值 * * @param object * @param excelParams * @param cell * @param titleString */ public Object getValue(IExcelDataHandler dataHanlder, Object object, Cell cell, Map excelParams, String titleString) throws Exception { ExcelImportEntity entity = excelParams.get(titleString); String xclass = "class java.lang.Object"; if (!(object instanceof Map)) { Method setMethod = entity.getMethods() != null && entity.getMethods().size() > 0 ? entity.getMethods().get(entity.getMethods().size() - 1) : entity.getMethod(); Type[] ts = setMethod.getGenericParameterTypes(); xclass = ts[0].toString(); } Object result = getCellValue(xclass, cell, entity); if (entity != null) { result = hanlderSuffix(entity.getSuffix(), result); //update-begin-author:taoYan date:20180807 for:多值替换 result = replaceValue(entity.getReplace(), result,entity.isMultiReplace()); //update-end-author:taoYan date:20180807 for:多值替换 } result = hanlderValue(dataHanlder, object, result, titleString); return getValueByType(xclass, result, entity); } /** * 获取cell值 * * @param dataHanlder * @param object * @param cellEntity * @param excelParams * @param titleString * @return */ public Object getValue(IExcelDataHandler dataHanlder, Object object, SaxReadCellEntity cellEntity, Map excelParams, String titleString) { ExcelImportEntity entity = excelParams.get(titleString); Method setMethod = entity.getMethods() != null && entity.getMethods().size() > 0 ? entity.getMethods().get(entity.getMethods().size() - 1) : entity.getMethod(); Type[] ts = setMethod.getGenericParameterTypes(); String xclass = ts[0].toString(); Object result = cellEntity.getValue(); result = hanlderSuffix(entity.getSuffix(), result); //update-begin-auhtor:taoyan date:20180807 for:多值替换 result = replaceValue(entity.getReplace(), result,entity.isMultiReplace()); //update-end-auhtor:taoyan date:20180807 for:多值替换 result = hanlderValue(dataHanlder, object, result, titleString); return getValueByType(xclass, result, entity); } /** * 把后缀删除掉 * * @param result * @param suffix * @return */ private Object hanlderSuffix(String suffix, Object result) { if (StringUtils.isNotEmpty(suffix) && result != null && result.toString().endsWith(suffix)) { String temp = result.toString(); return temp.substring(0, temp.length() - suffix.length()); } return result; } /** * 根据返回类型获取返回值 * * @param xclass * @param result * @param entity * @return */ private Object getValueByType(String xclass, Object result, ExcelImportEntity entity) { try { //update-begin-author:scott date:20180711 for:TASK #2950 【bug】excel 导入报错,空指针问题 if(result==null || "".equals(String.valueOf(result))){ return null; } //update-end-author:scott date:20180711 for:TASK #2950 【bug】excel 导入报错,空指针问题 if ("class java.util.Date".equals(xclass)) { return result; } if ("class java.lang.Boolean".equals(xclass) || "boolean".equals(xclass)) { //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 Boolean temp = Boolean.valueOf(String.valueOf(result)); //if(StringUtils.isNotEmpty(entity.getNumFormat())){ // return Boolean.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp)); //}else{ return temp; //} //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 } if ("class java.lang.Double".equals(xclass) || "double".equals(xclass)) { //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 Double temp = Double.valueOf(String.valueOf(result)); //if(StringUtils.isNotEmpty(entity.getNumFormat())){ // return Double.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp)); //}else{ return temp; //} //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 } if ("class java.lang.Long".equals(xclass) || "long".equals(xclass)) { //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 Long temp = Long.valueOf(ExcelUtil.remove0Suffix(String.valueOf(result))); //if(StringUtils.isNotEmpty(entity.getNumFormat())){ // return Long.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp)); //}else{ return temp; //} //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 } if ("class java.lang.Float".equals(xclass) || "float".equals(xclass)) { //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 Float temp = Float.valueOf(String.valueOf(result)); //if(StringUtils.isNotEmpty(entity.getNumFormat())){ // return Float.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp)); //}else{ return temp; //} //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 } if ("class java.lang.Integer".equals(xclass) || "int".equals(xclass)) { //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 Integer temp = Integer.valueOf(ExcelUtil.remove0Suffix(String.valueOf(result))); //if(StringUtils.isNotEmpty(entity.getNumFormat())){ // return Integer.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp)); //}else{ return temp; //} //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 } if ("class java.math.BigDecimal".equals(xclass)) { //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 BigDecimal temp = new BigDecimal(String.valueOf(result)); //if(StringUtils.isNotEmpty(entity.getNumFormat())){ // return new BigDecimal(new DecimalFormat(entity.getNumFormat()).format(temp)); //}else{ return temp; //} //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970 } if ("class java.lang.String".equals(xclass)) { // 针对String 类型,但是Excel获取的数据却不是String,比如Double类型,防止科学计数法 if (result instanceof String) { //---update-begin-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------ return ExcelUtil.remove0Suffix(result); //---update-end-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------ } // double类型防止科学计数法 if (result instanceof Double) { return PoiPublicUtil.doubleToString((Double) result); } //---update-begin-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------ return ExcelUtil.remove0Suffix(String.valueOf(result)); //---update-end-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------ } return result; } catch (Exception e) { LOGGER.error(e.getMessage(), e); throw new ExcelImportException(ExcelImportEnum.GET_VALUE_ERROR); } } /** * 调用处理接口处理值 * * @param dataHanlder * @param object * @param result * @param titleString * @return */ private Object hanlderValue(IExcelDataHandler dataHanlder, Object object, Object result, String titleString) { if (dataHanlder == null || dataHanlder.getNeedHandlerFields() == null || dataHanlder.getNeedHandlerFields().length == 0) { return result; } if (hanlderList == null) { hanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields()); } if (hanlderList.contains(titleString)) { return dataHanlder.importHandler(object, titleString, result); } return result; } //update-begin-author:taoyan date:20180807 for:导入多值替换-- /** * 导入支持多值替换 * @param replace 数据库中字典查询出来的数组 * @param result excel单元格获取的值 * @param multiReplace 是否支持多值替换 * @author taoYan * @since 2018年8月7日 */ private Object replaceValue(String[] replace, Object result,boolean multiReplace) { if(result == null){ return ""; } if(replace == null || replace.length<=0){ return result; } String temp = String.valueOf(result); String backValue = ""; if(temp.indexOf(",")>0 && multiReplace){ //原值中带有逗号,认为他是多值的 String multiReplaces[] = temp.split(","); for (String str : multiReplaces) { backValue = backValue.concat(replaceSingleValue(replace, str)+","); } if(backValue.equals("")){ backValue = temp; }else{ backValue = backValue.substring(0, backValue.length()-1); } }else{ backValue = replaceSingleValue(replace, temp); } //update-begin-author:liusq date:20210204 for:字典替换失败提示日志 if(replace.length>0 && backValue.equals(temp)){ LOGGER.warn("====================字典替换失败,字典值:{},要转换的导入值:{}====================", replace, temp); } //update-end-author:liusq date:20210204 for:字典替换失败提示日志 return backValue; } /** * 单值替换 ,若没找到则原值返回 */ private String replaceSingleValue(String[] replace, String temp){ String[] tempArr; for (int i = 0; i < replace.length; i++) { //update-begin---author:scott Date:20211220 for:[issues/I4MBB3]@Excel dicText字段的值有下划线时,导入功能不能正确解析--- //tempArr = replace[i].split("_"); tempArr = getValueArr(replace[i]); if (temp.equals(tempArr[0]) || temp.replace("_","---").equals(tempArr[0])) { //update-begin---author:wangshuai ---date:20220422 for:导入字典替换需要将---替换成_,不然数据库会存--- ------------ if(tempArr[1].contains("---")){ return tempArr[1].replace("---","_"); } //update-end---author:wangshuai ---date:20220422 for:导入字典替换需要将---替换成_,不然数据库会存--- -------------- return tempArr[1]; } //update-end---author:scott Date:20211220 for:[issues/I4MBB3]@Excel dicText字段的值有下划线时,导入功能不能正确解析--- } return temp; } //update-end-author:taoyan date:20180807 for:导入多值替换-- /** * 字典文本中含多个下划线横岗,取最后一个(解决空值情况) * * @param val * @return */ public String[] getValueArr(String val) { int i = val.lastIndexOf("_");//最后一个分隔符的位置 String[] c = new String[2]; c[0] = val.substring(0, i); //label c[1] = val.substring(i + 1); //key return c; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.imports; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.formula.functions.T; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.jeecgframework.core.util.ApplicationContextUtil; import org.jeecgframework.poi.excel.annotation.ExcelTarget; import org.jeecgframework.poi.excel.entity.ImportParams; import org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams; import org.jeecgframework.poi.excel.entity.params.ExcelImportEntity; import org.jeecgframework.poi.excel.entity.result.ExcelImportResult; import org.jeecgframework.poi.excel.entity.result.ExcelVerifyHanlderResult; import org.jeecgframework.poi.excel.imports.base.ImportBaseService; import org.jeecgframework.poi.excel.imports.base.ImportFileServiceI; import org.jeecgframework.poi.excel.imports.verifys.VerifyHandlerServer; import org.jeecgframework.poi.exception.excel.ExcelImportException; import org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum; import org.jeecgframework.poi.util.ExcelUtil; import org.jeecgframework.poi.util.PoiPublicUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Excel 导入服务 * * @author JEECG * @date 2014年6月26日 下午9:20:51 */ @SuppressWarnings({ "rawtypes", "unchecked", "hiding" }) public class ExcelImportServer extends ImportBaseService { private final static Logger LOGGER = LoggerFactory.getLogger(ExcelImportServer.class); private CellValueServer cellValueServer; private VerifyHandlerServer verifyHandlerServer; private boolean verfiyFail = false; //仅允许字母数字字符的正则表达式 private static final Pattern lettersAndNumbersPattern = Pattern.compile("^[a-zA-Z0-9]+$") ; /** * 异常数据styler */ private CellStyle errorCellStyle; public ExcelImportServer() { this.cellValueServer = new CellValueServer(); this.verifyHandlerServer = new VerifyHandlerServer(); } /*** * 向List里面继续添加元素 * * @param object * @param param * @param row * @param titlemap * @param targetId * @param pictures * @param params */ private void addListContinue(Object object, ExcelCollectionParams param, Row row, Map titlemap, String targetId, Map pictures, ImportParams params) throws Exception { Collection collection = (Collection) PoiPublicUtil.getMethod(param.getName(), object.getClass()).invoke(object, new Object[] {}); Object entity = PoiPublicUtil.createObject(param.getType(), targetId); String picId; boolean isUsed = false;// 是否需要加上这个对象 for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) { Cell cell = row.getCell(i); String titleString = (String) titlemap.get(i); if (param.getExcelParams().containsKey(titleString)) { if (param.getExcelParams().get(titleString).getType() == 2) { picId = row.getRowNum() + "_" + i; //update-begin---author:chenrui ---date:20240402 for:[issue/#6025/#6040]子表图片导入报错------------ saveImage(entity, picId, param.getExcelParams(), titleString, pictures, params); //update-end---author:chenrui ---date:20240402 for:[issue/#6025/#6040]子表图片导入报错------------ } else { saveFieldValue(params, entity, cell, param.getExcelParams(), titleString, row); } isUsed = true; } } if (isUsed) { collection.add(entity); } } /** * 获取key的值,针对不同类型获取不同的值 * * @Author JEECG * @date 2013-11-21 * @param cell * @return */ private String getKeyValue(Cell cell) { if(cell==null){ return null; } Object obj = null; switch (cell.getCellType()) { case STRING: obj = cell.getStringCellValue(); break; case BOOLEAN: obj = cell.getBooleanCellValue(); break; case NUMERIC: obj = cell.getNumericCellValue(); break; case FORMULA: obj = cell.getCellFormula(); break; } return obj == null ? null : obj.toString().trim(); } /** * 获取保存的真实路径 * * @param excelImportEntity * @param object * @return * @throws Exception */ private String getSaveUrl(ExcelImportEntity excelImportEntity, Object object) throws Exception { String url = ""; if (excelImportEntity.getSaveUrl().equals("upload")) { if (excelImportEntity.getMethods() != null && excelImportEntity.getMethods().size() > 0) { object = getFieldBySomeMethod(excelImportEntity.getMethods(), object); } url = object.getClass().getName().split("\\.")[object.getClass().getName().split("\\.").length - 1]; return excelImportEntity.getSaveUrl() + "/" + url.substring(0, url.lastIndexOf("Entity")); } return excelImportEntity.getSaveUrl(); } //update-begin--Author:xuelin Date:20171205 for:TASK #2098 【excel问题】 Online 一对多导入失败-------------------- private List importExcel(Collection result, Sheet sheet, Class pojoClass, ImportParams params, Map pictures) throws Exception { List collection = new ArrayList(); Map excelParams = new HashMap(); List excelCollection = new ArrayList(); String targetId = null; if (!Map.class.equals(pojoClass)) { Field fileds[] = PoiPublicUtil.getClassFields(pojoClass); ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class); if (etarget != null) { targetId = etarget.value(); } getAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null); } ignoreHeaderHandler(excelParams, params); Iterator rows = sheet.rowIterator(); Map titlemap = getTitleMap(sheet, rows, params, excelCollection); //update-begin-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex Set keys = excelParams.keySet(); for (String key : keys) { if (key.startsWith("FIXED_")) { String[] arr = key.split("_"); titlemap.put(Integer.parseInt(arr[1]), key); } } //update-end-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex Set columnIndexSet = titlemap.keySet(); Integer maxColumnIndex = Collections.max(columnIndexSet); Integer minColumnIndex = Collections.min(columnIndexSet); Row row = null; //跳过表头和标题行 //update-begin---author:chenrui ---date:20250715 for:[issues/3943]导入excel时,标题区域的空行会导致下方列表数据被吞------------ int skipRowNum = params.getTitleRows() + params.getHeadRows(); do { row = rows.next(); } while (row.getRowNum() != skipRowNum - 1); //update-end---author:chenrui ---date:20250715 for:[issues/3943]导入excel时,标题区域的空行会导致下方列表数据被吞------------ Object object = null; String picId; while (rows.hasNext() && (row == null || sheet.getLastRowNum() - row.getRowNum() > params.getLastOfInvalidRow())) { row = rows.next(); //update-begin--Author:xuelin Date:20171017 for:TASK #2373 【bug】表改造问题,导致 3.7.1批量导入用户bug-导入不成功-------------------- // 判断是集合元素还是不是集合元素,如果是就继续加入这个集合,不是就创建新的对象 //update-begin--Author:xuelin Date:20171206 for:TASK #2451 【excel导出bug】online 一对多导入成功, 但是现在代码生成后的一对多online导入有问题了 Cell keyIndexCell = row.getCell(params.getKeyIndex()); if (excelCollection.size()>0 && StringUtils.isEmpty(getKeyValue(keyIndexCell)) && object != null && !Map.class.equals(pojoClass)) { //update-end--Author:xuelin Date:20171206 for:TASK #2451 【excel导出bug】online 一对多导入成功, 但是现在代码生成后的一对多online导入有问题了 for (ExcelCollectionParams param : excelCollection) { addListContinue(object, param, row, titlemap, targetId, pictures, params); } } else { object = PoiPublicUtil.createObject(pojoClass, targetId); try { //update-begin-author:taoyan date:20200303 for:导入图片 int firstCellNum = row.getFirstCellNum(); if(firstCellNum>minColumnIndex){ firstCellNum = minColumnIndex; } //update-begin---author:chenrui ---date:20250320 for:[issues/7947]autopoi导入 报错Cell index must be >= 0 ------------ if (firstCellNum < 0) { firstCellNum = 0; } //update-end---author:chenrui ---date:20250320 for:[issues/7947]autopoi导入 报错Cell index must be >= 0 ------------ int lastCellNum = row.getLastCellNum(); if(lastCellNum excelParams,ImportParams params){ List ignoreList = new ArrayList<>(); for(String key:excelParams.keySet()){ String temp = excelParams.get(key).getGroupName(); if(temp!=null && temp.length()>0){ ignoreList.add(temp); } } params.setIgnoreHeaderList(ignoreList); } /** * 获取表格字段列名对应信息 * * @param rows * @param params * @param excelCollection * @return */ private Map getTitleMap(Sheet sheet, Iterator rows, ImportParams params, List excelCollection) throws Exception { Map titlemap = new HashMap(); Iterator cellTitle = null; String collectionName = null; ExcelCollectionParams collectionParams = null; Row headRow = null; int headBegin = params.getTitleRows(); //update_begin-author:taoyan date:2020622 for:当文件行数小于代码里设置的TitleRows时headRow一直为空就会出现死循环 int allRowNum = sheet.getPhysicalNumberOfRows(); //找到首行表头,每个sheet都必须至少有一行表头 while(headRow == null && headBegin < allRowNum){ headRow = sheet.getRow(headBegin++); } if(headRow==null){ throw new Exception("不识别该文件"); } //update-end-author:taoyan date:2020622 for:当文件行数小于代码里设置的TitleRows时headRow一直为空就会出现死循环 //设置表头行数 if (ExcelUtil.isMergedRegion(sheet, headRow.getRowNum(), 0)) { params.setHeadRows(2); }else{ params.setHeadRows(1); } cellTitle = headRow.cellIterator(); while (cellTitle.hasNext()) { Cell cell = cellTitle.next(); String value = getKeyValue(cell); if (StringUtils.isNotEmpty(value)) { titlemap.put(cell.getColumnIndex(), value);//加入表头列表 } } //多行表头 for (int j = headBegin; j < headBegin + params.getHeadRows()-1; j++) { headRow = sheet.getRow(j); cellTitle = headRow.cellIterator(); while (cellTitle.hasNext()) { Cell cell = cellTitle.next(); String value = getKeyValue(cell); if (StringUtils.isNotEmpty(value)) { int columnIndex = cell.getColumnIndex(); //当前cell的上一行是否为合并单元格 if(ExcelUtil.isMergedRegion(sheet, cell.getRowIndex()-1, columnIndex)){ collectionName = ExcelUtil.getMergedRegionValue(sheet, cell.getRowIndex()-1, columnIndex); if(params.isIgnoreHeader(collectionName)){ titlemap.put(cell.getColumnIndex(), value); }else{ titlemap.put(cell.getColumnIndex(), collectionName + "_" + value); } }else{ //update-begin-author:taoyan date:20220112 for: JT640 【online】导入 无论一对一还是一对多 如果子表只有一个字段 则子表无数据 // 上一行不是合并的情况下另有一种特殊的场景: 如果当前单元格和上面的单元格同一列 即子表字段只有一个 所以标题没有出现跨列 String prefixTitle = titlemap.get(cell.getColumnIndex()); if(prefixTitle!=null && !"".equals(prefixTitle)){ titlemap.put(cell.getColumnIndex(), prefixTitle + "_" +value); }else{ titlemap.put(cell.getColumnIndex(), value); } //update-end-author:taoyan date:20220112 for: JT640 【online】导入 无论一对一还是一对多 如果子表只有一个字段 则子表无数据 } /*int i = cell.getColumnIndex(); // 用以支持重名导入 if (titlemap.containsKey(i)) { collectionName = titlemap.get(i); collectionParams = getCollectionParams(excelCollection, collectionName); titlemap.put(i, collectionName + "_" + value); } else if (StringUtils.isNotEmpty(collectionName) && collectionParams.getExcelParams().containsKey(collectionName + "_" + value)) { titlemap.put(i, collectionName + "_" + value); } else { collectionName = null; collectionParams = null; } if (StringUtils.isEmpty(collectionName)) { titlemap.put(i, value); }*/ } } } return titlemap; } //update-end--Author:xuelin Date:20171205 for:TASK #2098 【excel问题】 Online 一对多导入失败-------------------- /** * 获取这个名称对应的集合信息 * * @param excelCollection * @param collectionName * @return */ private ExcelCollectionParams getCollectionParams(List excelCollection, String collectionName) { for (ExcelCollectionParams excelCollectionParams : excelCollection) { if (collectionName.equals(excelCollectionParams.getExcelName())) { return excelCollectionParams; } } return null; } /** * Excel 导入 field 字段类型 Integer,Long,Double,Date,String,Boolean * * @param inputstream * @param pojoClass * @param params * @return * @throws Exception */ public ExcelImportResult importExcelByIs(InputStream inputstream, Class pojoClass, ImportParams params) throws Exception { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Excel import start ,class is {}", pojoClass); } List result = new ArrayList(); Workbook book = null; boolean isXSSFWorkbook = false; //update-begin---author:chenrui ---date:20240403 for:[issue/#5987]嵌入单元格图片无法导入------------ // 复制输入流,防止在读取嵌入图片时流为空 ByteArrayOutputStream inCopy = new ByteArrayOutputStream(); IOUtils.copy(inputstream, inCopy); inputstream = new ByteArrayInputStream(inCopy.toByteArray()); if (!(inputstream.markSupported())) { inputstream = new PushbackInputStream(inputstream, 8); } //update-end---author:chenrui ---date:20240403 for:[issue/#5987]嵌入单元格图片无法导入------------ //begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】-------- //------poi4.x begin---- // FileMagic fm = FileMagic.valueOf(FileMagic.prepareToCheckMagic(inputstream)); // if(FileMagic.OLE2 == fm){ // isXSSFWorkbook=false; // } book = WorkbookFactory.create(inputstream); if(book instanceof XSSFWorkbook){ isXSSFWorkbook=true; } LOGGER.info(" >>> poi3升级到4.0兼容改造工作, isXSSFWorkbook = " +isXSSFWorkbook); //end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】-------- //begin-------author:liusq------date:20210313-----for:-------多sheet导入改造点-------- //获取导入文本的sheet数 //update-begin-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错 if(params.getSheetNum()==0){ int sheetNum = book.getNumberOfSheets(); if(sheetNum>0){ params.setSheetNum(sheetNum); } } //update-end-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错 //end-------author:liusq------date:20210313-----for:-------多sheet导入改造点-------- createErrorCellStyle(book); Map pictures; // 获取指定的sheet名称 String sheetName = params.getSheetName(); //update-begin-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数 for (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex() + params.getSheetNum(); i++) { //update-end-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数 //update-begin-author:taoyan date:2023-3-4 for: 导入数据支持指定sheet名称 if(sheetName!=null && !"".equals(sheetName)){ Sheet tempSheet = book.getSheetAt(i); if(!sheetName.equals(tempSheet.getSheetName())){ continue; } } //update-end-author:taoyan date:2023-3-4 for: 导入数据支持指定sheet名称 if (LOGGER.isDebugEnabled()) { LOGGER.debug(" start to read excel by is ,startTime is {}", System.currentTimeMillis()); } if (isXSSFWorkbook) { pictures = PoiPublicUtil.getSheetPictrues07((XSSFSheet) book.getSheetAt(i), (XSSFWorkbook) book); //update-begin---author:chenrui ---date:20240403 for:[issue/#5987]嵌入单元格图片无法导入------------ Map cellImages = PoiPublicUtil.getCellImages(book.getSheetAt(i), inCopy, book); if (!cellImages.isEmpty()) { pictures.putAll(cellImages); } //update-end---author:chenrui ---date:20240403 for:[issue/#5987]嵌入单元格图片无法导入------------ } else { pictures = PoiPublicUtil.getSheetPictrues03((HSSFSheet) book.getSheetAt(i), (HSSFWorkbook) book); } if (LOGGER.isDebugEnabled()) { LOGGER.debug(" end to read excel by is ,endTime is {}", new Date().getTime()); } result.addAll(importExcel(result, book.getSheetAt(i), pojoClass, params, pictures)); if (LOGGER.isDebugEnabled()) { LOGGER.debug(" end to read excel list by pos ,endTime is {}", new Date().getTime()); } } if (params.isNeedSave()) { saveThisExcel(params, pojoClass, isXSSFWorkbook, book); } return new ExcelImportResult(result, verfiyFail, book); } /** * * @param is * @return * @throws IOException */ public static byte[] getBytes(InputStream is) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int len; byte[] data = new byte[100000]; while ((len = is.read(data, 0, data.length)) != -1) { buffer.write(data, 0, len); } buffer.flush(); return buffer.toByteArray(); } /** * 保存字段值(获取值,校验值,追加错误信息) * * @param params * @param object * @param cell * @param excelParams * @param titleString * @param row * @throws Exception * @return */ private Object saveFieldValue(ImportParams params, Object object, Cell cell, Map excelParams, String titleString, Row row) throws Exception { Object value = cellValueServer.getValue(params.getDataHanlder(), object, cell, excelParams, titleString); if (object instanceof Map) { if (params.getDataHanlder() != null) { params.getDataHanlder().setMapValue((Map) object, titleString, value); } else { ((Map) object).put(titleString, value); } } else { ExcelVerifyHanlderResult verifyResult = verifyHandlerServer.verifyData(object, value, titleString, excelParams.get(titleString).getVerify(), params.getVerifyHanlder()); if (verifyResult.isSuccess()) { setValues(excelParams.get(titleString), object, value); } else { Cell errorCell = row.createCell(row.getLastCellNum()); errorCell.setCellValue(verifyResult.getMsg()); errorCell.setCellStyle(errorCellStyle); verfiyFail = true; throw new ExcelImportException(ExcelImportEnum.VERIFY_ERROR); } } return value; } /** * * @param object * @param picId * @param excelParams * @param titleString * @param pictures * @param params * @throws Exception */ private void saveImage(Object object, String picId, Map excelParams, String titleString, Map pictures, ImportParams params) throws Exception { if (pictures == null || pictures.get(picId)==null) { return; } PictureData image = pictures.get(picId); byte[] data = image.getData(); String fileName = "pic" + Math.round(Math.random() * 100000000000L); fileName += "." + PoiPublicUtil.getFileExtendName(data); //update-beign-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159 int saveType = excelParams.get(titleString).getSaveType(); if ( saveType == 1) { String path = PoiPublicUtil.getWebRootPath(getSaveUrl(excelParams.get(titleString), object)); File savefile = new File(path); if (!savefile.exists()) { savefile.mkdirs(); } savefile = new File(path + "/" + fileName); FileOutputStream fos = new FileOutputStream(savefile); fos.write(data); fos.close(); setValues(excelParams.get(titleString), object, getSaveUrl(excelParams.get(titleString), object) + "/" + fileName); } else if(saveType==2) { setValues(excelParams.get(titleString), object, data); } else { ImportFileServiceI importFileService = null; try { importFileService = ApplicationContextUtil.getContext().getBean(ImportFileServiceI.class); } catch (Exception e) { System.err.println(e.getMessage()); } if(importFileService!=null){ //update-beign-author:liusq date:20230411 for:【issue/4415】autopoi-web 导入图片字段时无法指定保存路径 String saveUrl = excelParams.get(titleString).getSaveUrl(); String dbPath; if(StringUtils.isNotBlank(saveUrl)){ LOGGER.debug("图片保存路径saveUrl = "+saveUrl); Matcher matcher = lettersAndNumbersPattern.matcher(saveUrl); if(!matcher.matches()){ LOGGER.warn("图片保存路径格式错误,只能设置字母和数字的组合!"); dbPath = importFileService.doUpload(data); }else{ dbPath = importFileService.doUpload(data,saveUrl); } }else{ dbPath = importFileService.doUpload(data); } //update-end-author:liusq date:20230411 for:【issue/4415】autopoi-web 导入图片字段时无法指定保存路径 setValues(excelParams.get(titleString), object, dbPath); } } //update-end-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159 } private void createErrorCellStyle(Workbook workbook) { errorCellStyle = workbook.createCellStyle(); Font font = workbook.createFont(); font.setColor(Font.COLOR_RED); errorCellStyle.setFont(font); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/base/ImportBaseService.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.imports.base; import java.io.File; import java.io.FileOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.core.util.ApplicationContextUtil; import org.jeecgframework.dict.service.AutoPoiDictServiceI; import org.jeecgframework.poi.excel.annotation.Excel; import org.jeecgframework.poi.excel.annotation.ExcelCollection; import org.jeecgframework.poi.excel.annotation.ExcelEntity; import org.jeecgframework.poi.excel.annotation.ExcelVerify; import org.jeecgframework.poi.excel.entity.ImportParams; import org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams; import org.jeecgframework.poi.excel.entity.params.ExcelImportEntity; import org.jeecgframework.poi.excel.entity.params.ExcelVerifyEntity; import org.jeecgframework.poi.util.PoiPublicUtil; /** * 导入基础和,普通方法和Sax共用 * * @author JEECG * @date 2015年1月9日 下午10:25:53 */ public class ImportBaseService { /** * 把这个注解解析放到类型对象中 * * @param targetId * @param field * @param excelEntity * @param pojoClass * @param getMethods * @param temp * @throws Exception */ public void addEntityToMap(String targetId, Field field, ExcelImportEntity excelEntity, Class pojoClass, List getMethods, Map temp) throws Exception { Excel excel = field.getAnnotation(Excel.class); excelEntity = new ExcelImportEntity(); excelEntity.setType(excel.type()); excelEntity.setSaveUrl(excel.savePath()); excelEntity.setSaveType(excel.imageType()); excelEntity.setReplace(excel.replace()); excelEntity.setDatabaseFormat(excel.databaseFormat()); excelEntity.setVerify(getImportVerify(field)); excelEntity.setSuffix(excel.suffix()); excelEntity.setNumFormat(excel.numFormat()); excelEntity.setGroupName(excel.groupName()); //update-begin-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex excelEntity.setFixedIndex(excel.fixedIndex()); //update-end-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex //update-begin-author:taoYan date:20180202 for:TASK #2067 【bug excel 问题】excel导入字典文本翻译问题 excelEntity.setMultiReplace(excel.multiReplace()); if(StringUtils.isNotEmpty(excel.dicCode())){ AutoPoiDictServiceI jeecgDictService = null; try { jeecgDictService = ApplicationContextUtil.getContext().getBean(AutoPoiDictServiceI.class); } catch (Exception e) { } if(jeecgDictService!=null){ String[] dictReplace = jeecgDictService.queryDict(excel.dictTable(), excel.dicCode(), excel.dicText()); if(excelEntity.getReplace()!=null && dictReplace!=null && dictReplace.length!=0){ excelEntity.setReplace(dictReplace); } } } //update-end-author:taoYan date:20180202 for:TASK #2067 【bug excel 问题】excel导入字典文本翻译问题 getExcelField(targetId, field, excelEntity, excel, pojoClass); if (getMethods != null) { List newMethods = new ArrayList(); newMethods.addAll(getMethods); newMethods.add(excelEntity.getMethod()); excelEntity.setMethods(newMethods); } //update-begin-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex if (excelEntity.getFixedIndex() != -1) { temp.put("FIXED_" + excelEntity.getFixedIndex(), excelEntity); } else { temp.put(excelEntity.getName(), excelEntity); } //update-end-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex } /** * 获取导入校验参数 * * @param field * @return */ public ExcelVerifyEntity getImportVerify(Field field) { ExcelVerify verify = field.getAnnotation(ExcelVerify.class); if (verify != null) { ExcelVerifyEntity entity = new ExcelVerifyEntity(); entity.setEmail(verify.isEmail()); entity.setInterHandler(verify.interHandler()); entity.setMaxLength(verify.maxLength()); entity.setMinLength(verify.minLength()); entity.setMobile(verify.isMobile()); entity.setNotNull(verify.notNull()); entity.setRegex(verify.regex()); entity.setRegexTip(verify.regexTip()); entity.setTel(verify.isTel()); return entity; } return null; } /** * 获取需要导出的全部字段 * * @param targetId * 目标ID * @param fields * @param excelCollection * @throws Exception */ public void getAllExcelField(String targetId, Field[] fields, Map excelParams, List excelCollection, Class pojoClass, List getMethods) throws Exception { ExcelImportEntity excelEntity = null; for (int i = 0; i < fields.length; i++) { Field field = fields[i]; if (PoiPublicUtil.isNotUserExcelUserThis(null, field, targetId)) { continue; } if (PoiPublicUtil.isCollection(field.getType())) { // 集合对象设置属性 ExcelCollectionParams collection = new ExcelCollectionParams(); collection.setName(field.getName()); Map temp = new HashMap(); ParameterizedType pt = (ParameterizedType) field.getGenericType(); Class clz = (Class) pt.getActualTypeArguments()[0]; collection.setType(clz); getExcelFieldList(targetId, PoiPublicUtil.getClassFields(clz), clz, temp, null); collection.setExcelParams(temp); collection.setExcelName(field.getAnnotation(ExcelCollection.class).name()); additionalCollectionName(collection); excelCollection.add(collection); } else if (PoiPublicUtil.isJavaClass(field)) { addEntityToMap(targetId, field, excelEntity, pojoClass, getMethods, excelParams); } else { List newMethods = new ArrayList(); if (getMethods != null) { newMethods.addAll(getMethods); } newMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass)); //update-begin-author:taoyan date:20210531 for:excel导入支持 注解@ExcelEntity显示合并表头 ExcelEntity excel = field.getAnnotation(ExcelEntity.class); if(excel.show()==true){ Map subExcelParams = new HashMap<>(); // 这里有个设计的坑,导出的时候最后一个参数是null, 即getgetMethods获取的是空,导入的时候需要设置层级getmethod getAllExcelField(targetId, PoiPublicUtil.getClassFields(field.getType()), subExcelParams, excelCollection, field.getType(), newMethods); for(String key: subExcelParams.keySet()){ excelParams.put(excel.name()+"_"+key, subExcelParams.get(key)); } }else{ getAllExcelField(targetId, PoiPublicUtil.getClassFields(field.getType()), excelParams, excelCollection, field.getType(), newMethods); } //update-end-author:taoyan date:20210531 for:excel导入支持 注解@ExcelEntity显示合并表头 } } } /** * 追加集合名称到前面 * * @param collection */ private void additionalCollectionName(ExcelCollectionParams collection) { Set keys = new HashSet(); keys.addAll(collection.getExcelParams().keySet()); for (String key : keys) { collection.getExcelParams().put(collection.getExcelName() + "_" + key, collection.getExcelParams().get(key)); collection.getExcelParams().remove(key); } } public void getExcelField(String targetId, Field field, ExcelImportEntity excelEntity, Excel excel, Class pojoClass) throws Exception { excelEntity.setName(getExcelName(excel.name(), targetId)); String fieldname = field.getName(); //update-begin-author:taoyan for:TASK #2798 【例子】导入扩展方法,支持自定义导入字段转换规则 excelEntity.setMethod(PoiPublicUtil.getMethod(fieldname, pojoClass, field.getType(),excel.importConvert())); //update-end-author:taoyan for:TASK #2798 【例子】导入扩展方法,支持自定义导入字段转换规则 if (StringUtils.isNotEmpty(excel.importFormat())) { excelEntity.setFormat(excel.importFormat()); } else { excelEntity.setFormat(excel.format()); } } public void getExcelFieldList(String targetId, Field[] fields, Class pojoClass, Map temp, List getMethods) throws Exception { ExcelImportEntity excelEntity = null; for (int i = 0; i < fields.length; i++) { Field field = fields[i]; if (PoiPublicUtil.isNotUserExcelUserThis(null, field, targetId)) { continue; } if (PoiPublicUtil.isJavaClass(field)) { addEntityToMap(targetId, field, excelEntity, pojoClass, getMethods, temp); } else { List newMethods = new ArrayList(); if (getMethods != null) { newMethods.addAll(getMethods); } newMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass, field.getType())); getExcelFieldList(targetId, PoiPublicUtil.getClassFields(field.getType()), field.getType(), temp, newMethods); } } } /** * 判断在这个单元格显示的名称 * * @param exportName * @param targetId * @return */ public String getExcelName(String exportName, String targetId) { if (exportName.indexOf("_") < 0) { return exportName; } if (StringUtils.isBlank(targetId)) { return exportName; } String[] arr = exportName.split(","); for (String str : arr) { if (str.indexOf(targetId) != -1) { //update-begin-author:liusq date:20210127 for:targetId问题处理 //return str.split("_")[0]; return str.replace("_"+targetId,""); //update-end-author:liusq date:20210127 for:targetId问题处理 } } return null; } public Object getFieldBySomeMethod(List list, Object t) throws Exception { Method m; for (int i = 0; i < list.size() - 1; i++) { m = list.get(i); t = m.invoke(t, new Object[] {}); } return t; } public void saveThisExcel(ImportParams params, Class pojoClass, boolean isXSSFWorkbook, Workbook book) throws Exception { String path = PoiPublicUtil.getWebRootPath(getSaveExcelUrl(params, pojoClass)); File savefile = new File(path); if (!savefile.exists()) { savefile.mkdirs(); } SimpleDateFormat format = new SimpleDateFormat("yyyMMddHHmmss"); FileOutputStream fos = new FileOutputStream(path + "/" + format.format(new Date()) + "_" + Math.round(Math.random() * 100000) + (isXSSFWorkbook == true ? ".xlsx" : ".xls")); book.write(fos); fos.close(); } /** * 获取保存的Excel 的真实路径 * * @param params * @param pojoClass * @return * @throws Exception */ public String getSaveExcelUrl(ImportParams params, Class pojoClass) throws Exception { String url = ""; if (params.getSaveUrl().equals("upload/excelUpload")) { url = pojoClass.getName().split("\\.")[pojoClass.getName().split("\\.").length - 1]; return params.getSaveUrl() + "/" + url; } return params.getSaveUrl(); } /** * 多个get 最后再set * * @param setMethods * @param object */ public void setFieldBySomeMethod(List setMethods, Object object, Object value) throws Exception { Object t = getFieldBySomeMethod(setMethods, object); setMethods.get(setMethods.size() - 1).invoke(t, value); } /** * * @param entity * @param object * @param value * @throws Exception */ public void setValues(ExcelImportEntity entity, Object object, Object value) throws Exception { if (entity.getMethods() != null) { setFieldBySomeMethod(entity.getMethods(), object, value); } else { entity.getMethod().invoke(object, value); } } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/base/ImportFileServiceI.java ================================================ package org.jeecgframework.poi.excel.imports.base; public interface ImportFileServiceI { /** * 上传文件 返回文件地址字符串 * @param data * @return */ String doUpload(byte[] data); /** * 上传文件 返回文件地址字符串 * @param data * @param saveUrl 保存路径 * @return */ String doUpload(byte[] data,String saveUrl); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/package-info.java ================================================ /** * 导入类 * @author JEECG * @date 2014年6月23日 下午11:05:59 */ package org.jeecgframework.poi.excel.imports; ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/SaxReadExcel.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.imports.sax; import java.io.InputStream; import java.util.Iterator; import java.util.List; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.model.SharedStrings; import org.jeecgframework.poi.excel.entity.ImportParams; import org.jeecgframework.poi.excel.imports.sax.parse.ISaxRowRead; import org.jeecgframework.poi.excel.imports.sax.parse.SaxRowRead; import org.jeecgframework.poi.exception.excel.ExcelImportException; import org.jeecgframework.poi.handler.inter.IExcelReadRowHanlder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; /** * 基于SAX Excel大数据读取,读取Excel 07版本,不支持图片读取 * * @author JEECG * @date 2014年12月29日 下午9:41:38 * @version 1.0 */ @SuppressWarnings("rawtypes") public class SaxReadExcel { private static final Logger LOGGER = LoggerFactory.getLogger(SaxReadExcel.class); public List readExcel(InputStream inputstream, Class pojoClass, ImportParams params, ISaxRowRead rowRead, IExcelReadRowHanlder hanlder) { try { OPCPackage opcPackage = OPCPackage.open(inputstream); return readExcel(opcPackage, pojoClass, params, rowRead, hanlder); } catch (Exception e) { LOGGER.error(e.getMessage(), e); throw new ExcelImportException(e.getMessage()); } } private List readExcel(OPCPackage opcPackage, Class pojoClass, ImportParams params, ISaxRowRead rowRead, IExcelReadRowHanlder hanlder) { try { XSSFReader xssfReader = new XSSFReader(opcPackage); SharedStrings sst = xssfReader.getSharedStringsTable(); if (rowRead == null) { rowRead = new SaxRowRead(pojoClass, params, hanlder); } XMLReader parser = fetchSheetParser(sst, rowRead); Iterator sheets = xssfReader.getSheetsData(); int sheetIndex = 0; while (sheets.hasNext() && sheetIndex < params.getSheetNum()) { sheetIndex++; InputStream sheet = sheets.next(); InputSource sheetSource = new InputSource(sheet); parser.parse(sheetSource); sheet.close(); } return rowRead.getList(); } catch (Exception e) { LOGGER.error(e.getMessage(), e); throw new ExcelImportException("SAX导入数据失败"); } } private XMLReader fetchSheetParser(SharedStrings sst, ISaxRowRead rowRead) throws SAXException { XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser"); ContentHandler handler = new SheetHandler(sst, rowRead); parser.setContentHandler(handler); return parser; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/SheetHandler.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.imports.sax; import java.math.BigDecimal; import java.util.Date; import java.util.List; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.xssf.model.SharedStrings; import org.jeecgframework.poi.excel.entity.enmus.CellValueType; import org.jeecgframework.poi.excel.entity.sax.SaxReadCellEntity; import org.jeecgframework.poi.excel.imports.sax.parse.ISaxRowRead; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.google.common.collect.Lists; /** * 回调接口 * * @author JEECG * @date 2014年12月29日 下午9:50:09 */ public class SheetHandler extends DefaultHandler { private SharedStrings sst; private String lastContents; // 当前行 private int curRow = 0; // 当前列 private int curCol = 0; private CellValueType type; private ISaxRowRead read; // 存储行记录的容器 private List rowlist = Lists.newArrayList(); public SheetHandler(SharedStrings sst, ISaxRowRead rowRead) { this.sst = sst; this.read = rowRead; } @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { // 置空 lastContents = ""; // c => 单元格 if ("c".equals(name)) { // 如果下一个元素是 SST 的索引,则将nextIsString标记为true String cellType = attributes.getValue("t"); if ("s".equals(cellType)) { type = CellValueType.String; return; } // 日期格式 cellType = attributes.getValue("s"); if ("1".equals(cellType)) { type = CellValueType.Date; } else if ("2".equals(cellType)) { type = CellValueType.Number; } } else if ("t".equals(name)) {// 当元素为t时 type = CellValueType.TElement; } } @Override public void endElement(String uri, String localName, String name) throws SAXException { // 根据SST的索引值的到单元格的真正要存储的字符串 // 这时characters()方法可能会被调用多次 if (CellValueType.String.equals(type)) { try { int idx = Integer.parseInt(lastContents); lastContents = sst.getItemAt(idx).toString(); } catch (Exception e) { } } // t元素也包含字符串 if (CellValueType.TElement.equals(type)) { String value = lastContents.trim(); rowlist.add(curCol, new SaxReadCellEntity(CellValueType.String, value)); curCol++; type = CellValueType.None; // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引 // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符 } else if ("v".equals(name)) { String value = lastContents.trim(); value = value.equals("") ? " " : value; if (CellValueType.Date.equals(type)) { Date date = DateUtil.getJavaDate(Double.valueOf(value)); rowlist.add(curCol, new SaxReadCellEntity(CellValueType.Date, date)); } else if (CellValueType.Number.equals(type)) { BigDecimal bd = new BigDecimal(value); rowlist.add(curCol, new SaxReadCellEntity(CellValueType.Number, bd)); } else if (CellValueType.String.equals(type)) { rowlist.add(curCol, new SaxReadCellEntity(CellValueType.String, value)); } curCol++; } else if (name.equals("row")) {// 如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法 read.parse(curRow, rowlist); rowlist.clear(); curRow++; curCol = 0; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { // 得到单元格内容的值 lastContents += new String(ch, start, length); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/parse/ISaxRowRead.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.imports.sax.parse; import java.util.List; import org.jeecgframework.poi.excel.entity.sax.SaxReadCellEntity; public interface ISaxRowRead { /** * 获取返回数据 * * @param * @return */ public List getList(); /** * 解析数据 * * @param index * @param datas */ public void parse(int index, List datas); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/parse/SaxRowRead.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.imports.sax.parse; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.jeecgframework.poi.excel.annotation.ExcelTarget; import org.jeecgframework.poi.excel.entity.ImportParams; import org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams; import org.jeecgframework.poi.excel.entity.params.ExcelImportEntity; import org.jeecgframework.poi.excel.entity.sax.SaxReadCellEntity; import org.jeecgframework.poi.excel.imports.CellValueServer; import org.jeecgframework.poi.excel.imports.base.ImportBaseService; import org.jeecgframework.poi.exception.excel.ExcelImportException; import org.jeecgframework.poi.handler.inter.IExcelReadRowHanlder; import org.jeecgframework.poi.util.PoiPublicUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; /** * 当行读取数据 * * @author JEECG * @param * @date 2015年1月1日 下午7:59:39 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class SaxRowRead extends ImportBaseService implements ISaxRowRead { private static final Logger LOGGER = LoggerFactory.getLogger(SaxRowRead.class); /** 需要返回的数据 **/ private List list; /** 导出的对象 **/ private Class pojoClass; /** 导入参数 **/ private ImportParams params; /** 列表头对应关系 **/ private Map titlemap = new HashMap(); /** 当前的对象 **/ private Object object = null; private Map excelParams = new HashMap(); private List excelCollection = new ArrayList(); private String targetId; private CellValueServer cellValueServer; private IExcelReadRowHanlder hanlder; public SaxRowRead(Class pojoClass, ImportParams params, IExcelReadRowHanlder hanlder) { list = Lists.newArrayList(); this.params = params; this.pojoClass = pojoClass; cellValueServer = new CellValueServer(); this.hanlder = hanlder; initParams(pojoClass, params); } private void initParams(Class pojoClass, ImportParams params) { try { Field fileds[] = PoiPublicUtil.getClassFields(pojoClass); ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class); if (etarget != null) { targetId = etarget.value(); } getAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null); } catch (Exception e) { LOGGER.error(e.getMessage(), e); throw new ExcelImportException(e.getMessage()); } } @Override public List getList() { return list; } @Override public void parse(int index, List datas) { try { if (datas == null || datas.size() == 0) { return; } // 标题行跳过 if (index < params.getTitleRows()) { return; } // 表头行 if (index < params.getTitleRows() + params.getHeadRows()) { addHeadData(datas); } else { addListData(datas); } } catch (Exception e) { LOGGER.error(e.getMessage(), e); throw new ExcelImportException(e.getMessage()); } } /** * 集合元素处理 * * @param datas */ private void addListData(List datas) throws Exception { // 判断是集合元素还是不是集合元素,如果是就继续加入这个集合,不是就创建新的对象 if ((datas.get(params.getKeyIndex()) == null || StringUtils.isEmpty(String.valueOf(datas.get(params.getKeyIndex()).getValue()))) && object != null) { for (ExcelCollectionParams param : excelCollection) { addListContinue(object, param, datas, titlemap, targetId, params); } } else { if (object != null && hanlder != null) { hanlder.hanlder(object); } object = PoiPublicUtil.createObject(pojoClass, targetId); SaxReadCellEntity entity; for (int i = 0, le = datas.size(); i < le; i++) { entity = datas.get(i); String titleString = (String) titlemap.get(i); if (excelParams.containsKey(titleString)) { saveFieldValue(params, object, entity, excelParams, titleString); } } for (ExcelCollectionParams param : excelCollection) { addListContinue(object, param, datas, titlemap, targetId, params); } if (hanlder == null) { list.add(object); } } } /*** * 向List里面继续添加元素 * * @param exclusions * @param object * @param param * @param datas * @param titlemap * @param targetId * @param params */ private void addListContinue(Object object, ExcelCollectionParams param, List datas, Map titlemap, String targetId, ImportParams params) throws Exception { Collection collection = (Collection) PoiPublicUtil.getMethod(param.getName(), object.getClass()).invoke(object, new Object[] {}); Object entity = PoiPublicUtil.createObject(param.getType(), targetId); boolean isUsed = false;// 是否需要加上这个对象 for (int i = 0; i < datas.size(); i++) { String titleString = (String) titlemap.get(i); if (param.getExcelParams().containsKey(titleString)) { saveFieldValue(params, entity, datas.get(i), param.getExcelParams(), titleString); isUsed = true; } } if (isUsed) { collection.add(entity); } } /** * 设置值 * * @param params * @param object * @param entity * @param excelParams * @param titleString * @throws Exception */ private void saveFieldValue(ImportParams params, Object object, SaxReadCellEntity entity, Map excelParams, String titleString) throws Exception { Object value = cellValueServer.getValue(params.getDataHanlder(), object, entity, excelParams, titleString); setValues(excelParams.get(titleString), object, value); } /** * put 表头数据 * * @param datas */ private void addHeadData(List datas) { for (int i = 0; i < datas.size(); i++) { if (StringUtils.isNotEmpty(String.valueOf(datas.get(i).getValue()))) { titlemap.put(i, String.valueOf(datas.get(i).getValue())); } } } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/verifys/BaseVerifyHandler.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.imports.verifys; import java.util.regex.Pattern; import org.jeecgframework.poi.excel.entity.result.ExcelVerifyHanlderResult; /** * 基础校验工具类 * * @author JEECG * @date 2014年6月23日 下午11:10:12 */ public class BaseVerifyHandler { private static String NOT_NULL = "不允许为空"; private static String IS_MOBILE = "不是手机号"; private static String IS_TEL = "不是电话号码"; private static String IS_EMAIL = "不是邮箱地址"; private static String MIN_LENGHT = "小于规定长度"; private static String MAX_LENGHT = "超过规定长度"; private static Pattern mobilePattern = Pattern.compile("^[1][3,4,5,8,7][0-9]{9}$"); private static Pattern telPattern = Pattern.compile("^([0][1-9]{2,3}-)?[0-9]{5,10}$"); private static Pattern emailPattern = Pattern.compile("^([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+\\.[a-zA-Z]{2,3}$"); /** * email校验 * * @param name * @param val * @return */ public static ExcelVerifyHanlderResult isEmail(String name, Object val) { if (!emailPattern.matcher(String.valueOf(val)).matches()) { return new ExcelVerifyHanlderResult(false, name + IS_EMAIL); } return new ExcelVerifyHanlderResult(true); } /** * 手机校验 * * @param name * @param val * @return */ public static ExcelVerifyHanlderResult isMobile(String name, Object val) { if (!mobilePattern.matcher(String.valueOf(val)).matches()) { return new ExcelVerifyHanlderResult(false, name + IS_MOBILE); } return new ExcelVerifyHanlderResult(true); } /** * 电话校验 * * @param name * @param val * @return */ public static ExcelVerifyHanlderResult isTel(String name, Object val) { if (!telPattern.matcher(String.valueOf(val)).matches()) { return new ExcelVerifyHanlderResult(false, name + IS_TEL); } return new ExcelVerifyHanlderResult(true); } /** * 最大长度校验 * * @param name * @param val * @return */ public static ExcelVerifyHanlderResult maxLength(String name, Object val, int maxLength) { if (notNull(name, val).isSuccess() && String.valueOf(val).length() > maxLength) { return new ExcelVerifyHanlderResult(false, name + MAX_LENGHT); } return new ExcelVerifyHanlderResult(true); } /** * 最小长度校验 * * @param name * @param val * @param minLength * @return */ public static ExcelVerifyHanlderResult minLength(String name, Object val, int minLength) { if (notNull(name, val).isSuccess() && String.valueOf(val).length() < minLength) { return new ExcelVerifyHanlderResult(false, name + MIN_LENGHT); } return new ExcelVerifyHanlderResult(true); } /** * 非空校验 * * @param name * @param val * @return */ public static ExcelVerifyHanlderResult notNull(String name, Object val) { if (val == null || val.toString().equals("")) { return new ExcelVerifyHanlderResult(false, name + NOT_NULL); } return new ExcelVerifyHanlderResult(true); } /** * 正则表达式校验 * * @param name * @param val * @param regex * @param regexTip * @return */ public static ExcelVerifyHanlderResult regex(String name, Object val, String regex, String regexTip) { Pattern pattern = Pattern.compile(regex); if (!pattern.matcher(String.valueOf(val)).matches()) { return new ExcelVerifyHanlderResult(false, name + regexTip); } return new ExcelVerifyHanlderResult(true); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/excel/imports/verifys/VerifyHandlerServer.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.imports.verifys; import org.apache.commons.lang3.StringUtils; import org.jeecgframework.poi.excel.entity.params.ExcelVerifyEntity; import org.jeecgframework.poi.excel.entity.result.ExcelVerifyHanlderResult; import org.jeecgframework.poi.handler.inter.IExcelVerifyHandler; /** * 校验服务 * * @author JEECG * @date 2014年6月29日 下午4:37:56 */ public class VerifyHandlerServer { private final static ExcelVerifyHanlderResult DEFAULT_RESULT = new ExcelVerifyHanlderResult(true); private void addVerifyResult(ExcelVerifyHanlderResult hanlderResult, ExcelVerifyHanlderResult result) { if (!hanlderResult.isSuccess()) { result.setSuccess(false); result.setMsg((StringUtils.isEmpty(result.getMsg()) ? "" : result.getMsg() + " , ") + hanlderResult.getMsg()); } } /** * 校驗數據 * * @param object * @param value * @param titleString * @param verify * @param excelVerifyHandler */ public ExcelVerifyHanlderResult verifyData(Object object, Object value, String name, ExcelVerifyEntity verify, IExcelVerifyHandler excelVerifyHandler) { if (verify == null) { return DEFAULT_RESULT; } ExcelVerifyHanlderResult result = new ExcelVerifyHanlderResult(true, ""); if (verify.isNotNull()) { addVerifyResult(BaseVerifyHandler.notNull(name, value), result); } if (verify.isEmail()) { addVerifyResult(BaseVerifyHandler.isEmail(name, value), result); } if (verify.isMobile()) { addVerifyResult(BaseVerifyHandler.isMobile(name, value), result); } if (verify.isTel()) { addVerifyResult(BaseVerifyHandler.isTel(name, value), result); } if (verify.getMaxLength() != -1) { addVerifyResult(BaseVerifyHandler.maxLength(name, value, verify.getMaxLength()), result); } if (verify.getMinLength() != -1) { addVerifyResult(BaseVerifyHandler.minLength(name, value, verify.getMinLength()), result); } if (StringUtils.isNotEmpty(verify.getRegex())) { addVerifyResult(BaseVerifyHandler.regex(name, value, verify.getRegex(), verify.getRegexTip()), result); } if (verify.isInterHandler()) { addVerifyResult(excelVerifyHandler.verifyHandler(object, name, value), result); } return result; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/exception/excel/ExcelExportException.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.exception.excel; import org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum; /** * 导出异常 * * @author JEECG * @date 2014年6月19日 下午10:56:18 */ public class ExcelExportException extends RuntimeException { private static final long serialVersionUID = 1L; private ExcelExportEnum type; public ExcelExportException() { super(); } public ExcelExportException(ExcelExportEnum type) { super(type.getMsg()); this.type = type; } public ExcelExportException(ExcelExportEnum type, Throwable cause) { super(type.getMsg(), cause); } public ExcelExportException(String message) { super(message); } public ExcelExportException(String message, ExcelExportEnum type) { super(message); this.type = type; } public ExcelExportEnum getType() { return type; } public void setType(ExcelExportEnum type) { this.type = type; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/exception/excel/ExcelImportException.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.exception.excel; import org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum; /** * 导入异常 * * @author JEECG * @date 2014年6月29日 下午2:23:43 */ public class ExcelImportException extends RuntimeException { private static final long serialVersionUID = 1L; private ExcelImportEnum type; public ExcelImportException() { super(); } public ExcelImportException(ExcelImportEnum type) { super(type.getMsg()); this.type = type; } public ExcelImportException(ExcelImportEnum type, Throwable cause) { super(type.getMsg(), cause); this.type = type; } public ExcelImportException(String message) { super(message); } public ExcelImportException(String message, ExcelImportEnum type) { super(message); this.type = type; } public ExcelImportEnum getType() { return type; } public void setType(ExcelImportEnum type) { this.type = type; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/exception/excel/enums/ExcelExportEnum.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.exception.excel.enums; /** * 导出异常类型枚举 * * @author JEECG * @date 2014年6月19日 下午10:59:51 */ public enum ExcelExportEnum { PARAMETER_ERROR("Excel 导出 参数错误"), EXPORT_ERROR("Excel导出错误"),TEMPLATE_ERROR ("Excel 模板错误");; private String msg; ExcelExportEnum(String msg) { this.msg = msg; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/exception/excel/enums/ExcelImportEnum.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.exception.excel.enums; /** * 导出异常类型枚举 * * @author JEECG * @date 2014年6月19日 下午10:59:51 */ public enum ExcelImportEnum { GET_VALUE_ERROR("Excel 值获取失败"), VERIFY_ERROR("值校验失败"); private String msg; ExcelImportEnum(String msg) { this.msg = msg; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/exception/word/WordExportException.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.exception.word; import org.jeecgframework.poi.exception.word.enmus.WordExportEnum; /** * word导出异常 * * @author JEECG * @date 2014年8月9日 下午10:32:51 */ public class WordExportException extends RuntimeException { private static final long serialVersionUID = 1L; public WordExportException() { super(); } public WordExportException(String msg) { super(msg); } public WordExportException(WordExportEnum exception) { super(exception.getMsg()); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/exception/word/enmus/WordExportEnum.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.exception.word.enmus; /** * 导出异常枚举 * * @author JEECG * @date 2014年8月9日 下午10:34:58 */ public enum WordExportEnum { EXCEL_PARAMS_ERROR("Excel 导出 参数错误"), EXCEL_HEAD_HAVA_NULL("Excel 表头 有的字段为空"), EXCEL_NO_HEAD("Excel 没有表头"); private String msg; WordExportEnum(String msg) { this.msg = msg; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/handler/impl/ExcelDataHandlerDefaultImpl.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.handler.impl; import java.util.Map; import org.jeecgframework.poi.handler.inter.IExcelDataHandler; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.Hyperlink; /** * 数据处理默认实现,返回空 * * @author JEECG * @date 2014年6月20日 上午12:11:52 */ public abstract class ExcelDataHandlerDefaultImpl implements IExcelDataHandler { /** * 需要处理的字段 */ private String[] needHandlerFields; @Override public Object exportHandler(Object obj, String name, Object value) { return value; } @Override public String[] getNeedHandlerFields() { return needHandlerFields; } @Override public Object importHandler(Object obj, String name, Object value) { return value; } @Override public void setNeedHandlerFields(String[] needHandlerFields) { this.needHandlerFields = needHandlerFields; } @Override public void setMapValue(Map map, String originKey, Object value) { map.put(originKey, value); } @Override public Hyperlink getHyperlink(CreationHelper creationHelper, Object obj, String name, Object value) { return null; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/handler/impl/package-info.java ================================================ /** * 对接口的抽象的默认实现,避免用户实现过多方法 * @author JEECG * @date 2014年6月20日 上午12:09:27 */ package org.jeecgframework.poi.handler.impl; ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelDataHandler.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.handler.inter; import java.util.Map; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.Hyperlink; /** * Excel 导入导出 数据处理接口 * * @author JEECG * @date 2014年6月19日 下午11:59:45 */ public interface IExcelDataHandler { /** * 导出处理方法 * * @param obj * 当前对象 * @param name * 当前字段名称 * @param value * 当前值 * @return */ public Object exportHandler(Object obj, String name, Object value); /** * 获取需要处理的字段,导入和导出统一处理了, 减少书写的字段 * * @return */ public String[] getNeedHandlerFields(); /** * 导入处理方法 当前对象,当前字段名称,当前值 * * @param obj * 当前对象 * @param name * 当前字段名称 * @param value * 当前值 * @return */ public Object importHandler(Object obj, String name, Object value); /** * 设置需要处理的属性列表 * * @param fields */ public void setNeedHandlerFields(String[] fields); /** * 设置Map导入,自定义 put * * @param map * @param originKey * @param value */ public void setMapValue(Map map, String originKey, Object value); /** * 获取这个字段的 Hyperlink ,07版本需要,03版本不需要 * @param creationHelper * @param obj * @param name * @param value * @return */ public Hyperlink getHyperlink(CreationHelper creationHelper, Object obj, String name, Object value); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelDictHandler.java ================================================ package org.jeecgframework.poi.handler.inter; /** * 字典翻译处理 * @author liusq * @date 2022-05-27 */ public interface IExcelDictHandler { /** * 从值翻译到名称 * * @param dict 字典Key * @param obj 对象 * @param name 属性名称 * @param value 属性值 * @return */ public String toName(String dict, Object obj, String name, Object value); /** * 从名称翻译到值 * * @param dict 字典Key * @param obj 对象 * @param name 属性名称 * @param value 属性值 * @return */ public String toValue(String dict, Object obj, String name, Object value); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelExportServer.java ================================================ package org.jeecgframework.poi.handler.inter; import java.util.List; /** * 导出数据接口 * @Description [LOWCOD-2521]【autopoi】大数据导出方法【全局】 * @author liusq * @date 2022年1月4号 */ public interface IExcelExportServer { /** * 查询数据接口 * * @param queryParams 查询条件 * @param page 当前页数从1开始 * @data 2022年1月4号 * @return */ public List selectListForExcelExport(Object queryParams, int page); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelExportServerEnhanced.java ================================================ package org.jeecgframework.poi.handler.inter; import java.util.List; /** * 增强的导出数据接口 - 支持游标分页,解决大数据量导出性能问题 * for [QQYUN-13964]演示系统数据量大,点击没反应 * * 推荐实现方式: * 1. 使用主键ID或其他有序字段作为游标 * 2. 每次查询条件: WHERE id > lastId ORDER BY id LIMIT pageSize * 3. 避免使用 LIMIT offset, size 这种会随着offset增大而变慢的方式 * * @Description 解决40万+数据导出查询效率问题 * @author chenrui * @date 2025-11-03 */ public interface IExcelExportServerEnhanced { /** * 基于游标的分页查询 - 高性能方案 * * 实现示例: *
     * public List selectListForExcelExport(Object queryParams, SysLog lastRecord, int pageSize) {
     *     Long lastId = lastRecord != null ? ((YourEntity)lastRecord).getId() : 0L;
     *     return mapper.selectList(new QueryWrapper()
     *         .gt("id", lastId)
     *         .orderByAsc("id")
     *         .last("LIMIT " + pageSize));
     * }
     * 
* * @param queryParams 查询条件 * @param lastRecord 上一批次的最后一条记录(首次查询时为null) * @param pageSize 每批次查询数量 * @return 当前批次的数据列表,返回null或空列表表示没有更多数据 */ List selectListForExcelExport(Object queryParams, T lastRecord, int pageSize); /** * 获取默认的每批次查询数量 * 可以根据业务情况调整,建议5000-20000之间 * * @return 每批次查询数量,默认10000 */ default int getPageSize() { return 10000; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelReadRowHanlder.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.handler.inter; /** * 接口自定义处理类 * * @author JEECG * @date 2015年1月16日 下午8:06:26 * @param */ public interface IExcelReadRowHanlder { /** * 处理解析对象 * * @param t */ public void hanlder(T t); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelVerifyHandler.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.handler.inter; import org.jeecgframework.poi.excel.entity.result.ExcelVerifyHanlderResult; /** * 导入校验接口 * * @author JEECG * @date 2014年6月23日 下午11:08:21 */ public interface IExcelVerifyHandler { /** * 获取需要处理的字段,导入和导出统一处理了, 减少书写的字段 * * @return */ public String[] getNeedVerifyFields(); /** * 获取需要处理的字段,导入和导出统一处理了, 减少书写的字段 * * @return */ public void setNeedVerifyFields(String[] arr); /** * 导出处理方法 * * @param obj * 当前对象 * @param name * 当前字段名称 * @param value * 当前值 * @return */ public ExcelVerifyHanlderResult verifyHandler(Object obj, String name, Object value); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IWriter.java ================================================ package org.jeecgframework.poi.handler.inter; import java.util.Collection; /** * 大数据写出服务接口 * * @Description [LOWCOD-2521]【autopoi】大数据导出方法【全局】 * @author liusq * @date 2022年1月4号 */ public interface IWriter { /** * 获取输出对象 * * @return */ default public T get() { return null; } /** * 写入数据 * * @param data * @return */ public IWriter write(Collection data); /** * 关闭流,完成业务 * * @return */ public T close(); } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/handler/package-info.java ================================================ /** * 数据处理中心,对导入导出进行数据处理 * @author JEECG * @date 2014年6月20日 上午12:08:09 */ package org.jeecgframework.poi.handler; ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/package-info.java ================================================ /** * @author JEECG * @date 2014年2月10日 * @version 1.0 * 对POI进行封装,通过注解的使用,完成POI的简易重复操作 * 通过模板完成较为复杂的操作 * 进阶步骤:
*

1.了解注解,图片类

*

2.模板语言

*

3.模板组合

* */ package org.jeecgframework.poi; ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/ExcelUtil.java ================================================ package org.jeecgframework.poi.util; /** * @author Link Xue * @version 20171025 * POI对EXCEL操作工具 */ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.poi.EncryptedDocumentException; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.util.CellRangeAddress; public class ExcelUtil { public static void main(String[] args){ //读取excel数据 ArrayList> result = ExcelUtil.readExcelToObj("D:\\上传表.xlsx"); for(Map map:result){ System.out.println(map); } } /** * 读取excel数据 * @param path */ public static ArrayList> readExcelToObj(String path) { Workbook wb = null; ArrayList> result = null; try { wb = WorkbookFactory.create(new File(path)); result = readExcel(wb, 0, 2, 0); } catch (Exception e) { e.printStackTrace(); } return result; } /** * 读取excel文件 * @param wb * @param sheetIndex sheet页下标:从0开始 * @param startReadLine 开始读取的行:从0开始 * @param tailLine 去除最后读取的行 */ public static ArrayList> readExcel(Workbook wb,int sheetIndex, int startReadLine, int tailLine) { Sheet sheet = wb.getSheetAt(sheetIndex); Row row = null; ArrayList> result = new ArrayList>(); for(int i=startReadLine; i map = new HashMap(); for(Cell c : row) { String returnStr = ""; boolean isMerge = isMergedRegion(sheet, i, c.getColumnIndex()); //判断是否具有合并单元格 if(isMerge) { String rs = getMergedRegionValue(sheet, row.getRowNum(), c.getColumnIndex()); // System.out.print(rs + "------ "); returnStr = rs; }else { // System.out.print(c.getRichStringCellValue()+"++++ "); returnStr = c.getRichStringCellValue().getString(); } if(c.getColumnIndex()==0){ map.put("id",returnStr); }else if(c.getColumnIndex()==1){ map.put("base",returnStr); }else if(c.getColumnIndex()==2){ map.put("siteName",returnStr); }else if(c.getColumnIndex()==3){ map.put("articleName",returnStr); }else if(c.getColumnIndex()==4){ map.put("mediaName",returnStr); }else if(c.getColumnIndex()==5){ map.put("mediaUrl",returnStr); }else if(c.getColumnIndex()==6){ map.put("newsSource",returnStr); }else if(c.getColumnIndex()==7){ map.put("isRecord",returnStr); }else if(c.getColumnIndex()==8){ map.put("recordTime",returnStr); }else if(c.getColumnIndex()==9){ map.put("remark",returnStr); } } result.add(map); // System.out.println(); } return result; } /** * 获取合并单元格的值 * @param sheet * @param row * @param column * @return */ public static String getMergedRegionValue(Sheet sheet ,int row , int column){ int sheetMergeCount = sheet.getNumMergedRegions(); for(int i = 0 ; i < sheetMergeCount ; i++){ CellRangeAddress ca = sheet.getMergedRegion(i); int firstColumn = ca.getFirstColumn(); int lastColumn = ca.getLastColumn(); int firstRow = ca.getFirstRow(); int lastRow = ca.getLastRow(); if(row >= firstRow && row <= lastRow){ if(column >= firstColumn && column <= lastColumn){ Row fRow = sheet.getRow(firstRow); Cell fCell = fRow.getCell(firstColumn); return getCellValue(fCell) ; } } } return null ; } /** * 判断合并了行 * @param sheet * @param row * @param column * @return */ public static boolean isMergedRow(Sheet sheet,int row ,int column) { int sheetMergeCount = sheet.getNumMergedRegions(); for (int i = 0; i < sheetMergeCount; i++) { CellRangeAddress range = sheet.getMergedRegion(i); int firstColumn = range.getFirstColumn(); int lastColumn = range.getLastColumn(); int firstRow = range.getFirstRow(); int lastRow = range.getLastRow(); if(row == firstRow && row == lastRow){ if(column >= firstColumn && column <= lastColumn){ return true; } } } return false; } /** * 判断指定的单元格是否是合并单元格 * @param sheet * @param row 行下标 * @param column 列下标 * @return */ public static boolean isMergedRegion(Sheet sheet,int row ,int column) { int sheetMergeCount = sheet.getNumMergedRegions(); for (int i = 0; i < sheetMergeCount; i++) { CellRangeAddress range = sheet.getMergedRegion(i); int firstColumn = range.getFirstColumn(); int lastColumn = range.getLastColumn(); int firstRow = range.getFirstRow(); int lastRow = range.getLastRow(); if(row >= firstRow && row <= lastRow){ if(column >= firstColumn && column <= lastColumn){ return true; } } } return false; } /** * 判断sheet页中是否含有合并单元格 * @param sheet * @return */ public static boolean hasMerged(Sheet sheet) { return sheet.getNumMergedRegions() > 0 ? true : false; } /** * 合并单元格 * @param sheet * @param firstRow 开始行 * @param lastRow 结束行 * @param firstCol 开始列 * @param lastCol 结束列 */ public static void mergeRegion(Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) { //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B try{ sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol)); }catch (IllegalArgumentException e){ e.fillInStackTrace(); } //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B } /** * 获取单元格的值 * @param cell * @return */ public static String getCellValue(Cell cell){ if(cell == null) { return ""; } if(cell.getCellType() == CellType.STRING){ return cell.getStringCellValue(); }else if(cell.getCellType() == CellType.BOOLEAN){ return String.valueOf(cell.getBooleanCellValue()); }else if(cell.getCellType() == CellType.FORMULA){ return cell.getCellFormula() ; }else if(cell.getCellType() == CellType.NUMERIC){ return String.valueOf(cell.getNumericCellValue()); } return ""; } /** * 数字值,去掉.0后缀 * @param cell * @return */ public static String remove0Suffix(Object value){ if(value!=null) { // update-begin-author:taoyan date:20210526 for:对于特殊的字符串 V1.0 也进行了去.0操作 这是不合理的 String val = value.toString(); if(val.endsWith(".0") && isNumberString(val)) { val = val.replace(".0", ""); } // update-end-author:taoyan date:20210526 for:对于特殊的字符串 V1.0 也进行了去.0操作 这是不合理的 return val; } return null; } /** * 判断给定的字符串是不是只有数字 * @param str * @return */ private static boolean isNumberString(String str){ //update-begin---author:chenrui ---date:20240424 for:[QQYUN-9048]负数被识别成非数字------------ String regex = "^-?[0-9]+\\.0+$"; //update-end---author:chenrui ---date:20240424 for:[QQYUN-9048]负数被识别成非数字------------ Pattern pattern = Pattern.compile(regex); Matcher m = pattern.matcher(str); if (m.find()) { return true; } return false; } /** * 从excel读取内容 */ public static void readContent(String fileName) { boolean isE2007 = false; //判断是否是excel2007格式 if(fileName.endsWith("xlsx")) isE2007 = true; try { InputStream input = new FileInputStream(fileName); //建立输入流 Workbook wb = null; //根据文件格式(2003或者2007)来初始化 if(isE2007) wb = new XSSFWorkbook(input); else wb = new HSSFWorkbook(input); Sheet sheet = wb.getSheetAt(0); //获得第一个表单 Iterator rows = sheet.rowIterator(); //获得第一个表单的迭代器 while (rows.hasNext()) { Row row = rows.next(); //获得行数据 System.out.println("Row #" + row.getRowNum()); //获得行号从0开始 Iterator cells = row.cellIterator(); //获得第一行的迭代器 while (cells.hasNext()) { Cell cell = cells.next(); System.out.println("Cell #" + cell.getColumnIndex()); switch (cell.getCellType()) { //根据cell中的类型来输出数据 case NUMERIC: System.out.println(cell.getNumericCellValue()); break; case STRING: System.out.println(cell.getStringCellValue()); break; case BOOLEAN: System.out.println(cell.getBooleanCellValue()); break; case FORMULA: System.out.println(cell.getCellFormula()); break; default: System.out.println("unsuported sell type======="+cell.getCellType()); break; } } } } catch (IOException ex) { ex.printStackTrace(); } } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/JsonParser.java ================================================ package org.jeecgframework.poi.util; import java.util.*; import java.lang.reflect.*; import java.math.BigDecimal; import java.math.BigInteger; /** * json格式处理 */ public class JsonParser { /** * 将JSON数组字符串解析为List * @param json JSON数组字符串 * @return List集合,包含解析后的对象 */ public static List parseJsonArrayToList(String json) { if (json == null || json.trim().isEmpty()) { return new ArrayList<>(); } String trimmed = json.trim(); if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) { throw new IllegalArgumentException("Input is not a valid JSON array"); } // 移除外层的方括号 String content = trimmed.substring(1, trimmed.length() - 1).trim(); if (content.isEmpty()) { return new ArrayList<>(); } List result = new ArrayList<>(); List elements = splitJsonElements(content); for (String element : elements) { result.add(parseJsonValue(element.trim())); } return result; } /** * 分割JSON数组中的元素 */ private static List splitJsonElements(String content) { List elements = new ArrayList<>(); StringBuilder current = new StringBuilder(); int braceDepth = 0; int bracketDepth = 0; boolean inString = false; char stringChar = '"'; boolean escapeNext = false; for (int i = 0; i < content.length(); i++) { char c = content.charAt(i); if (escapeNext) { current.append(c); escapeNext = false; continue; } if (c == '\\') { escapeNext = true; current.append(c); continue; } if (!inString) { if (c == ',') { if (braceDepth == 0 && bracketDepth == 0) { elements.add(current.toString()); current = new StringBuilder(); continue; } } else if (c == '{') { braceDepth++; } else if (c == '}') { braceDepth--; } else if (c == '[') { bracketDepth++; } else if (c == ']') { bracketDepth--; } else if (c == '"' || c == '\'') { inString = true; stringChar = c; } } else { if (c == stringChar) { inString = false; } } current.append(c); } if (current.length() > 0) { elements.add(current.toString()); } return elements; } /** * 解析JSON值 */ private static Object parseJsonValue(String jsonValue) { if (jsonValue == null || jsonValue.isEmpty()) { return null; } String trimmed = jsonValue.trim(); // 处理null if ("null".equalsIgnoreCase(trimmed)) { return null; } // 处理布尔值 if ("true".equalsIgnoreCase(trimmed)) { return true; } if ("false".equalsIgnoreCase(trimmed)) { return false; } // 处理字符串 if (isQuotedString(trimmed)) { return parseString(trimmed); } // 处理数字 if (isNumber(trimmed)) { return parseNumber(trimmed); } // 处理对象 if (trimmed.startsWith("{")) { return parseJsonObject(trimmed); } // 处理数组 if (trimmed.startsWith("[")) { return parseJsonArray(trimmed); } // 如果都不是,尝试作为数字处理,否则返回字符串 try { return parseNumber(trimmed); } catch (NumberFormatException e) { return trimmed; } } /** * 解析JSON对象 */ private static Map parseJsonObject(String json) { if (!json.startsWith("{") || !json.endsWith("}")) { throw new IllegalArgumentException("Invalid JSON object: " + json); } String content = json.substring(1, json.length() - 1).trim(); if (content.isEmpty()) { return new LinkedHashMap<>(); } Map result = new LinkedHashMap<>(); List pairs = splitJsonPairs(content); for (String pair : pairs) { String[] keyValue = splitKeyValue(pair.trim()); if (keyValue.length == 2) { String key = parseString(keyValue[0].trim()); Object value = parseJsonValue(keyValue[1].trim()); result.put(key, value); } } return result; } /** * 解析JSON数组 */ private static List parseJsonArray(String json) { if (!json.startsWith("[") || !json.endsWith("]")) { throw new IllegalArgumentException("Invalid JSON array: " + json); } String content = json.substring(1, json.length() - 1).trim(); if (content.isEmpty()) { return new ArrayList<>(); } List result = new ArrayList<>(); List elements = splitJsonElements(content); for (String element : elements) { result.add(parseJsonValue(element.trim())); } return result; } /** * 分割JSON对象中的键值对 */ private static List splitJsonPairs(String content) { List pairs = new ArrayList<>(); StringBuilder current = new StringBuilder(); int braceDepth = 0; int bracketDepth = 0; boolean inString = false; char stringChar = '"'; boolean escapeNext = false; for (int i = 0; i < content.length(); i++) { char c = content.charAt(i); if (escapeNext) { current.append(c); escapeNext = false; continue; } if (c == '\\') { escapeNext = true; current.append(c); continue; } if (!inString) { if (c == ',') { if (braceDepth == 0 && bracketDepth == 0) { pairs.add(current.toString()); current = new StringBuilder(); continue; } } else if (c == '{') { braceDepth++; } else if (c == '}') { braceDepth--; } else if (c == '[') { bracketDepth++; } else if (c == ']') { bracketDepth--; } else if (c == '"' || c == '\'') { inString = true; stringChar = c; } } else { if (c == stringChar) { inString = false; } } current.append(c); } if (current.length() > 0) { pairs.add(current.toString()); } return pairs; } /** * 分割键值对 */ private static String[] splitKeyValue(String pair) { int colonIndex = -1; boolean inString = false; char stringChar = '"'; boolean escapeNext = false; for (int i = 0; i < pair.length(); i++) { char c = pair.charAt(i); if (escapeNext) { escapeNext = false; continue; } if (c == '\\') { escapeNext = true; continue; } if (!inString) { if (c == ':') { colonIndex = i; break; } else if (c == '"' || c == '\'') { inString = true; stringChar = c; } } else { if (c == stringChar) { inString = false; } } } if (colonIndex == -1) { throw new IllegalArgumentException("Invalid key-value pair: " + pair); } return new String[] { pair.substring(0, colonIndex), pair.substring(colonIndex + 1) }; } /** * 解析字符串,处理转义字符 */ private static String parseString(String str) { if (!isQuotedString(str)) { return str; } char quoteChar = str.charAt(0); String content = str.substring(1, str.length() - 1); StringBuilder result = new StringBuilder(); for (int i = 0; i < content.length(); i++) { char c = content.charAt(i); if (c == '\\' && i + 1 < content.length()) { char next = content.charAt(i + 1); switch (next) { case '"': result.append('"'); i++; break; case '\'': result.append('\''); i++; break; case '\\': result.append('\\'); i++; break; case '/': result.append('/'); i++; break; case 'b': result.append('\b'); i++; break; case 'f': result.append('\f'); i++; break; case 'n': result.append('\n'); i++; break; case 'r': result.append('\r'); i++; break; case 't': result.append('\t'); i++; break; case 'u': // Unicode转义 if (i + 5 < content.length()) { String hex = content.substring(i + 2, i + 6); try { int codePoint = Integer.parseInt(hex, 16); result.append((char) codePoint); i += 5; } catch (NumberFormatException e) { result.append(c); } } else { result.append(c); } break; default: result.append(c); } } else { result.append(c); } } return result.toString(); } /** * 解析数字 */ private static Object parseNumber(String numStr) { try { // 尝试解析为整数 if (numStr.indexOf('.') == -1 && numStr.indexOf('e') == -1 && numStr.indexOf('E') == -1) { try { long longValue = Long.parseLong(numStr); if (longValue >= Integer.MIN_VALUE && longValue <= Integer.MAX_VALUE) { return (int) longValue; } return longValue; } catch (NumberFormatException e) { // 可能是大整数 return new BigInteger(numStr); } } else { // 尝试解析为浮点数 double doubleValue = Double.parseDouble(numStr); if (Math.abs(doubleValue) <= Float.MAX_VALUE) { float floatValue = (float) doubleValue; // 检查是否实际上是整数 if (floatValue == Math.floor(floatValue) && !Double.isInfinite(floatValue)) { if (floatValue >= Integer.MIN_VALUE && floatValue <= Integer.MAX_VALUE) { return (int) floatValue; } else { return (long) floatValue; } } return floatValue; } return doubleValue; } } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid number: " + numStr, e); } } /** * 判断是否为引号包裹的字符串 */ private static boolean isQuotedString(String str) { return (str.startsWith("\"") && str.endsWith("\"")) || (str.startsWith("'") && str.endsWith("'")); } /** * 判断是否为数字 */ private static boolean isNumber(String str) { if (str == null || str.isEmpty()) { return false; } String trimmed = str.trim(); if (trimmed.isEmpty()) { return false; } char firstChar = trimmed.charAt(0); if (firstChar != '-' && firstChar != '+' && !Character.isDigit(firstChar)) { return false; } boolean hasDecimalPoint = false; boolean hasExponent = false; boolean hasDigit = false; for (int i = (firstChar == '-' || firstChar == '+') ? 1 : 0; i < trimmed.length(); i++) { char c = trimmed.charAt(i); if (c >= '0' && c <= '9') { hasDigit = true; } else if (c == '.') { if (hasDecimalPoint || hasExponent) { return false; } hasDecimalPoint = true; } else if (c == 'e' || c == 'E') { if (hasExponent || !hasDigit) { return false; } hasExponent = true; hasDigit = false; // 指数后面必须有数字 } else if (c == '+' || c == '-') { // 只允许在指数符号后出现 char prev = (i > 0) ? trimmed.charAt(i - 1) : '\0'; if (!(prev == 'e' || prev == 'E')) { return false; } } else { return false; } } return hasDigit; } // 测试方法 public static void main(String[] args) { // 测试用例1:基本数组 String json1 = "[{\"name\":\"John\",\"age\":30}, {\"name\":\"Jane\",\"age\":25}]"; List result1 = parseJsonArrayToList(json1); System.out.println("Test 1 Result: " + result1); // 测试用例2:嵌套结构 String json2 = "[{\"name\":\"John\",\"scores\":[85,90,78],\"address\":{\"city\":\"NY\",\"zip\":10001}}]"; List result2 = parseJsonArrayToList(json2); System.out.println("Test 2 Result: " + result2); // 测试用例3:各种数据类型 String json3 = "[123, 45.67, \"hello\", true, false, null]"; List result3 = parseJsonArrayToList(json3); System.out.println("Test 3 Result: " + result3); // 测试用例4:转义字符 String json4 = "[\"Line1\\nLine2\", \"Quote:\\\"Hello\\\"\", \"Backslash:\\\\\"]"; List result4 = parseJsonArrayToList(json4); System.out.println("Test 4 Result: " + result4); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/MyX509TrustManager.java ================================================ package org.jeecgframework.poi.util; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; /** * https请求用到 */ public class MyX509TrustManager implements X509TrustManager{ @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { // TODO Auto-generated method stub } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { // TODO Auto-generated method stub } @Override public X509Certificate[] getAcceptedIssuers() { // TODO Auto-generated method stub return null; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/PoiCellUtil.java ================================================ /** * */ package org.jeecgframework.poi.util; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; /** * 处理单元格的数值 * @Description [LOWCOD-2521]【autopoi】大数据导出方法【全局】 * @author liusq * @date 2022年1月4号 */ public class PoiCellUtil { /** * 读取单元格的值 * * @param sheet * @param row * @param column * @return */ public static String getCellValue(Sheet sheet, int row, int column) { String value = null; if (isMergedRegion(sheet, row, column)) { value = getMergedRegionValue(sheet, row, column); } else { Row rowData = sheet.getRow(row); Cell cell = rowData.getCell(column); value = getCellValue(cell); } return value; } /** * 获取合并单元格的值 * * @param sheet * @param row * @param column * @return */ public static String getMergedRegionValue(Sheet sheet, int row, int column) { int sheetMergeCount = sheet.getNumMergedRegions(); for (int i = 0; i < sheetMergeCount; i++) { CellRangeAddress ca = sheet.getMergedRegion(i); int firstColumn = ca.getFirstColumn(); int lastColumn = ca.getLastColumn(); int firstRow = ca.getFirstRow(); int lastRow = ca.getLastRow(); if (row >= firstRow && row <= lastRow) { if (column >= firstColumn && column <= lastColumn) { Row fRow = sheet.getRow(firstRow); Cell fCell = fRow.getCell(firstColumn); return getCellValue(fCell); } } } return null; } /** * 判断指定的单元格是否是合并单元格 * * @param sheet * @param row * @param column * @return */ public static boolean isMergedRegion(Sheet sheet, int row, int column) { int sheetMergeCount = sheet.getNumMergedRegions(); for (int i = 0; i < sheetMergeCount; i++) { CellRangeAddress ca = sheet.getMergedRegion(i); int firstColumn = ca.getFirstColumn(); int lastColumn = ca.getLastColumn(); int firstRow = ca.getFirstRow(); int lastRow = ca.getLastRow(); if (row >= firstRow && row <= lastRow) { if (column >= firstColumn && column <= lastColumn) { return true; } } } return false; } /** * 获取单元格的值 * _NONE(-1), * NUMERIC(0), * STRING(1), * FORMULA(2), * BLANK(3), * BOOLEAN(4), * ERROR(5); * @param cell * @return */ public static String getCellValue(Cell cell) { if (cell == null) { return ""; } if (cell.getCellType() == CellType.STRING) { return cell.getStringCellValue(); } else if (cell.getCellType() == CellType.BOOLEAN) { return String.valueOf(cell.getBooleanCellValue()); } else if (cell.getCellType() == CellType.FORMULA) { try { return cell.getCellFormula(); } catch (Exception e) { return String.valueOf(cell.getNumericCellValue()); } } else if (cell.getCellType() == CellType.NUMERIC) { return String.valueOf(cell.getNumericCellValue()); } else if (cell.getCellType() == CellType.ERROR) { return String.valueOf(cell.getErrorCellValue()); } return ""; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/PoiElUtil.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.util; import java.util.Collection; import java.util.Map; import org.jeecgframework.poi.exception.excel.ExcelExportException; /** * AutoPoi的el 表达式支持工具类 * * @author JEECG * @date 2015年4月25日 下午12:13:21 */ public final class PoiElUtil { public static final String LENGTH = "le:"; public static final String FOREACH = "fe:"; public static final String FOREACH_NOT_CREATE = "!fe:"; public static final String FOREACH_AND_SHIFT = "$fe:"; public static final String FOREACH_COL = "#fe:"; public static final String FOREACH_COL_VALUE = "v_fe:"; public static final String START_STR = "{{"; public static final String END_STR = "}}"; public static final String WRAP = "]]"; public static final String NUMBER_SYMBOL = "n:"; public static final String FORMAT_DATE = "fd:"; public static final String FORMAT_NUMBER = "fn:"; public static final String IF_DELETE = "!if:"; public static final String EMPTY = ""; public static final String CONST = "'"; public static final String NULL = "&NULL&"; public static final String LEFT_BRACKET = "("; public static final String RIGHT_BRACKET = ")"; public static final String DICT_HANDLER = "dict:"; private PoiElUtil() { } /** * 解析字符串,支持 le,fd,fn,!if,三目 * * @param obj * @param map * @return * @throws Exception */ public static Object eval(String text, Map map) throws Exception { String tempText = new String(text); Object obj = innerEval(text, map); // 如果没有被处理而且这个值找map中存在就处理这个值 if (tempText.equals(obj.toString())) { if (map.containsKey(tempText.split("\\.")[0])) { return PoiPublicUtil.getParamsValue(text, map); } else { return ""; } } return obj; } /** * 解析字符串,支持 le,fd,fn,!if,三目 * * @param obj * @param map * @return * @throws Exception */ public static Object innerEval(String text, Map map) throws Exception { if (text.indexOf("?") != -1 && text.indexOf(":") != -1) { return trinocular(text, map); } if (text.indexOf(LENGTH) != -1) { return length(text, map); } if (text.indexOf(FORMAT_DATE) != -1) { return formatDate(text, map); } if (text.indexOf(FORMAT_NUMBER) != -1) { return formatNumber(text, map); } if (text.indexOf(IF_DELETE) != -1) { return ifDelete(text, map); } if (text.startsWith("'")) { return text.replace("'", ""); } return text; } /** * 是不是删除列 * * @param text * @param map * @return * @throws Exception */ private static Object ifDelete(String text, Map map) throws Exception { // 把多个空格变成一个空格 text = text.replaceAll("\\s{1,}", " ").trim(); String[] keys = getKey(IF_DELETE, text).split(" "); text = text.replace(IF_DELETE, EMPTY); return isTrue(keys, map); } /** * 是不是真 * 这个方法两个地方用到 * 1.三目表达式的判断,表达式需要设置空格 {{field == 1? field1:field2 }} 或者 {{field?field1:field2 }} * 2.取非表达式(判断为真则当前excel的一整列会被干掉) {{!if:(field == 1)}} 或者 {{!if:(field)}} * * 如果字段field本身就能判断true或者false 他会走len==1的逻辑处理 * 如果字段field需要结合其他固定值来判断true或者false 那么记住一定要再表达式里打空格 然后他会split空格 走len==3的逻辑处理 * @param keys * @param map * @return * @throws Exception */ private static Boolean isTrue(String[] keys, Map map) throws Exception { //update-author:taoyan date:20200622 for:此处判断len当为1 if (keys.length == 1) { String constant = null; if ((constant = isConstant(keys[0])) != null) { return Boolean.valueOf(constant); } return Boolean.valueOf(PoiPublicUtil.getParamsValue(keys[0], map).toString()); } if (keys.length == 3) { if(keys[0]==null || keys[2]==null){ return false; } Object first = String.valueOf(eval(keys[0], map)); Object second = String.valueOf(eval(keys[2], map)); return PoiFunctionUtil.isTrue(first, keys[1], second); } throw new ExcelExportException("判断参数不对"); } /** * 判断是不是常量 * * @param string * @return */ private static String isConstant(String param) { if (param.indexOf("'") != -1) { return param.replace("'", ""); } return null; } /** * 格式化数字 * * @param text * @param map * @return * @throws Exception */ private static Object formatNumber(String text, Map map) throws Exception { String[] key = getKey(FORMAT_NUMBER, text).split(";"); text = text.replace(FORMAT_NUMBER, EMPTY); return innerEval(replacinnerEvalue(text, PoiFunctionUtil.formatNumber(PoiPublicUtil.getParamsValue(key[0], map), key[1])), map); } /** * 格式化时间 * * @param text * @param map * @return * @throws Exception */ private static Object formatDate(String text, Map map) throws Exception { String[] key = getKey(FORMAT_DATE, text).split(";"); text = text.replace(FORMAT_DATE, EMPTY); return innerEval(replacinnerEvalue(text, PoiFunctionUtil.formatDate(PoiPublicUtil.getParamsValue(key[0], map), key[1])), map); } /** * 计算这个的长度 * * @param text * @param map * @throws Exception */ private static Object length(String text, Map map) throws Exception { String key = getKey(LENGTH, text); text = text.replace(LENGTH, EMPTY); Object val = PoiPublicUtil.getParamsValue(key, map); return innerEval(replacinnerEvalue(text, PoiFunctionUtil.length(val)), map); } private static String replacinnerEvalue(String text, Object val) { StringBuilder sb = new StringBuilder(); sb.append(text.substring(0, text.indexOf(LEFT_BRACKET))); sb.append(" "); sb.append(val); sb.append(" "); sb.append(text.substring(text.indexOf(RIGHT_BRACKET) + 1, text.length())); return sb.toString().trim(); } private static String getKey(String prefix, String text) { int leftBracket = 1, rigthBracket = 0, position = 0; int index = text.indexOf(prefix) + prefix.length(); while (text.charAt(index) == " ".charAt(0)) { text = text.substring(0, index) + text.substring(index + 1, text.length()); } for (int i = text.indexOf(prefix + LEFT_BRACKET) + prefix.length() + 1; i < text.length(); i++) { if (text.charAt(i) == LEFT_BRACKET.charAt(0)) { leftBracket++; } if (text.charAt(i) == RIGHT_BRACKET.charAt(0)) { rigthBracket++; } if (leftBracket == rigthBracket) { position = i; break; } } return text.substring(text.indexOf(prefix + LEFT_BRACKET) + 1 + prefix.length(), position).trim(); } public static void main(String[] args) { System.out.println(getKey(IF_DELETE, "测试 " + IF_DELETE + " (小明)")); } /** * 三目运算 * * @return * @throws Exception */ private static Object trinocular(String text, Map map) throws Exception { //update-begin-author:liusq---date:2024-08-07--for: [issues/6925]autopoi通过word模板生成word时:三目、求长、常量、日期转换没起效果 //把多个空格变成一个空格 text = text.replaceAll("\\s{1,}", " ").trim(); String testText = text.substring(0, text.indexOf("?")); text = text.substring(text.indexOf("?") + 1, text.length()).trim(); text = innerEval(text, map).toString(); String[] keys = text.split(":"); Object first = null, second = null; if (keys.length > 2) { if (keys[0].trim().contains("?")) { String trinocular = keys[0]; for (int i = 1; i < keys.length - 1; i++) { trinocular += ":" + keys[i]; } first = evalNoParse(trinocular, map); second = evalNoParse(keys[keys.length - 1].trim(), map); } else { first = evalNoParse(keys[0].trim(), map); String trinocular = keys[1]; for (int i = 2; i < keys.length; i++) { trinocular += ":" + keys[i]; } second = evalNoParse(trinocular, map); } } else { first = evalNoParse(keys[0].trim(), map); second = evalNoParse(keys[1].trim(), map); } return isTrue(testText.split(" "), map) ? first : second; //update-end-author:liusq---date:2024-08-07--for: [issues/6925]autopoi通过word模板生成word时:三目、求长、常量、日期转换没起效果 } /** * 解析字符串,支持 le,fd,fn,!if,三目 找不到返回原值 * * @param text * @param map * @return * @throws Exception */ public static Object evalNoParse(String text, Map map) throws Exception { String tempText = new String(text); Object obj = innerEval(text, map); //如果没有被处理而且这个值找map中存在就处理这个值,找不到就返回空字符串 if (tempText.equals(obj.toString())) { if (map.containsKey(tempText.split("\\.")[0])) { return PoiPublicUtil.getParamsValue(tempText, map); } else { return obj; } } return obj; } /** * 解析字符串, 不支持 le,fd,fn,!if,三目 ,获取是集合的字段前缀 * * @param text * @param map * @return * @throws Exception */ public static String evalFindName(String text, Map map) throws Exception { String[] keys = text.split("\\."); StringBuilder sb = new StringBuilder().append(keys[0]); for (int i = 1; i < keys.length; i++) { sb.append(".").append(keys[i]); if (eval(sb.toString(), map) instanceof Collection) { return sb.toString(); } } return null; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/PoiExcelGraphDataUtil.java ================================================ package org.jeecgframework.poi.util; import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Sheet; import org.jeecgframework.poi.excel.graph.entity.ExcelGraph; import org.jeecgframework.poi.excel.graph.entity.ExcelGraphElement; import java.util.List; /** * 构建特殊数据结构 * @Description [LOWCOD-2521]【autopoi】大数据导出方法【全局】 * @author liusq * @date 2022年1月4号 */ public class PoiExcelGraphDataUtil { /** * 构建获取数据最后行数 并写入到定义对象中 * @param dataSourceSheet * @param graph */ public static void buildGraphData(Sheet dataSourceSheet, ExcelGraph graph) { if (graph != null && graph.getCategory() != null && graph.getValueList() != null && graph.getValueList().size() > 0) { graph.getCategory().setEndRowNum(dataSourceSheet.getLastRowNum()); for (ExcelGraphElement e : graph.getValueList()) { if (e != null) { e.setEndRowNum(dataSourceSheet.getLastRowNum()); } } } } /** * 构建多个图形对象 * @param dataSourceSheet * @param graphList */ public static void buildGraphData(Sheet dataSourceSheet, List graphList) { if (graphList != null && graphList.size() > 0) { for (ExcelGraph graph : graphList) { buildGraphData(dataSourceSheet, graph); } } } /** * 获取画布,没有就创建一个 * @param sheet * @return */ public static Drawing getDrawingPatriarch(Sheet sheet){ if(sheet.getDrawingPatriarch() == null){ sheet.createDrawingPatriarch(); } return sheet.getDrawingPatriarch(); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/PoiExcelTempUtil.java ================================================ package org.jeecgframework.poi.util; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.*; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * poi 4.0 07版本在 shift操作下有bug,不移动了单元格以及单元格样式,没有移动cell * cell还是复用的原理的cell,导致wb输出的时候没有输出值 * 等待修复的时候删除这个问题 * * @author by jueyue on 19-6-17. */ public class PoiExcelTempUtil { /** * 把这N行的数据,cell重新设置下,修复因为shift的浅复制问题,导致文本不显示的错误 * * @param sheet * @param startRow * @param endRow */ public static void reset(Sheet sheet, int startRow, int endRow) { if (sheet.getWorkbook() instanceof HSSFWorkbook) { return; } for (int i = startRow; i <= endRow; i++) { Row row = sheet.getRow(i); if (row == null) { continue; } int cellNum = row.getLastCellNum(); for (int j = 0; j < cellNum; j++) { if (row.getCell(j) == null) { continue; } Map map = copyCell(row.getCell(j)); row.removeCell(row.getCell(j)); Cell cell = row.createCell(j); cell.setCellStyle((CellStyle) map.get("cellStyle")); if ((boolean) map.get("isDate")) { cell.setCellValue((Date) map.get("value")); } else { CellType cellType = (CellType) map.get("cellType"); switch (cellType) { case NUMERIC: cell.setCellValue((double) map.get("value")); break; case STRING: cell.setCellValue((String) map.get("value")); break; case FORMULA: cell.setCellFormula((String) map.get("value")); break; case BLANK: break; case BOOLEAN: cell.setCellValue((boolean) map.get("value")); break; case ERROR: break; } } } } } private static Map copyCell(Cell cell) { Map map = new HashMap<>(); map.put("cellType", cell.getCellType()); map.put("isDate", CellType.NUMERIC == cell.getCellType() && DateUtil.isCellDateFormatted(cell)); map.put("value", getValue(cell)); map.put("cellStyle", cell.getCellStyle()); return map; } private static Object getValue(Cell cell) { if (CellType.NUMERIC == cell.getCellType() && DateUtil.isCellDateFormatted(cell)) { return cell.getDateCellValue(); } switch (cell.getCellType()) { case _NONE: return null; case NUMERIC: return cell.getNumericCellValue(); case STRING: return cell.getStringCellValue(); case FORMULA: return cell.getCellFormula(); case BLANK: break; case BOOLEAN: return cell.getBooleanCellValue(); case ERROR: break; } return null; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/PoiFunctionUtil.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.util; import java.lang.reflect.Array; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Map; import org.jeecgframework.poi.exception.excel.ExcelExportException; /** * if else,length,for each,fromatNumber,formatDate 满足模板的 el 表达式支持 * * @author JEECG * @date 2015年4月24日 下午8:04:02 */ public final class PoiFunctionUtil { private static final String TWO_DECIMAL_STR = "###.00"; private static final String THREE_DECIMAL_STR = "###.000"; private static final DecimalFormat TWO_DECIMAL = new DecimalFormat(TWO_DECIMAL_STR); private static final DecimalFormat THREE_DECIMAL = new DecimalFormat(THREE_DECIMAL_STR); private static final String DAY_STR = "yyyy-MM-dd"; private static final String TIME_STR = "yyyy-MM-dd HH:mm:ss"; private static final String TIME__NO_S_STR = "yyyy-MM-dd HH:mm"; private static final SimpleDateFormat DAY_FORMAT = new SimpleDateFormat(DAY_STR); private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat(TIME_STR); private static final SimpleDateFormat TIME__NO_S_FORMAT = new SimpleDateFormat(TIME__NO_S_STR); private PoiFunctionUtil() { } /** * 获取对象的长度 * * @param obj * @return */ @SuppressWarnings("rawtypes") public static int length(Object obj) { if (obj == null) { return 0; } if (obj instanceof Map) { return ((Map) obj).size(); } if (obj instanceof Collection) { return ((Collection) obj).size(); } if (obj.getClass().isArray()) { return Array.getLength(obj); } return String.valueOf(obj).length(); } /** * 格式化数字 * * @param obj * @throws NumberFormatException * @return */ public static String formatNumber(Object obj, String format) { if (obj == null || obj.toString() == "") { return ""; } double number = Double.valueOf(obj.toString()); DecimalFormat decimalFormat = null; if (TWO_DECIMAL.equals(format)) { decimalFormat = TWO_DECIMAL; } else if (THREE_DECIMAL_STR.equals(format)) { decimalFormat = THREE_DECIMAL; } else { decimalFormat = new DecimalFormat(format); } return decimalFormat.format(number); } /** * 格式化时间 * * @param obj * @return */ public static String formatDate(Object obj, String format) { if (obj == null || obj.toString() == "") { return ""; } SimpleDateFormat dateFormat = null; if (DAY_STR.equals(format)) { dateFormat = DAY_FORMAT; } else if (TIME_STR.equals(format)) { dateFormat = TIME_FORMAT; } else if (TIME__NO_S_STR.equals(format)) { dateFormat = TIME__NO_S_FORMAT; } else { dateFormat = new SimpleDateFormat(format); } return dateFormat.format(obj); } /** * 判断是不是成功 * * @param first * @param operator * @param second * @return */ public static boolean isTrue(Object first, String operator, Object second) { if (">".endsWith(operator)) { return isGt(first, second); } else if ("<".endsWith(operator)) { return isGt(second, first); } else if ("==".endsWith(operator)) { if (first != null && second != null) { return first.equals(second); } return first == second; } else if ("!=".endsWith(operator)) { if (first != null && second != null) { return !first.equals(second); } return first != second; } else { throw new ExcelExportException("占不支持改操作符"); } } /** * 前者是不是大于后者 * * @param first * @param second * @return */ private static boolean isGt(Object first, Object second) { if (first == null || first.toString() == "") { return false; } if (second == null || second.toString() == "") { return true; } double one = Double.valueOf(first.toString()); double two = Double.valueOf(second.toString()); return one > two; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/PoiMergeCellUtil.java ================================================ package org.jeecgframework.poi.util; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; import org.jeecgframework.poi.excel.entity.params.MergeEntity; import org.jeecgframework.poi.exception.excel.ExcelExportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 纵向合并单元格工具类 * * @author JEECG * @date 2015年6月21日 上午11:21:40 */ public final class PoiMergeCellUtil { private final static Logger LOGGER = LoggerFactory.getLogger(PoiMergeCellUtil.class); private PoiMergeCellUtil() { } /** * 纵向合并相同内容的单元格 * * @param sheet * @param startRow * 开始行 * @param columns * 需要处理的列 */ public static void mergeCells(Sheet sheet, int startRow, Integer... columns) { if (columns == null) { throw new ExcelExportException("至少需要处理1列"); } Map mergeMap = new HashMap(); for (int i = 0; i < columns.length; i++) { mergeMap.put(columns[i], null); } mergeCells(sheet, mergeMap, startRow, sheet.getLastRowNum()); } /** * 纵向合并相同内容的单元格 * * @param sheet * @param mergeMap * key--列,value--依赖的列,没有传空 * @param startRow * 开始行 */ public static void mergeCells(Sheet sheet, Map mergeMap, int startRow) { mergeCells(sheet, mergeMap, startRow, sheet.getLastRowNum()); } /** * 纵向合并相同内容的单元格 * * @param sheet * @param mergeMap * key--列,value--依赖的列,没有传空 * @param startRow * 开始行 * @param endRow * 结束行 */ public static void mergeCells(Sheet sheet, Map mergeMap, int startRow, int endRow) { Map mergeDataMap = new HashMap(); if (mergeMap.size() == 0) { return; } Row row; Set sets = mergeMap.keySet(); String text; for (int i = startRow; i <= endRow; i++) { row = sheet.getRow(i); for (Integer index : sets) { if (row == null || row.getCell(index) == null) { if (mergeDataMap.get(index) == null) { continue; } if (mergeDataMap.get(index).getEndRow() == 0) { mergeDataMap.get(index).setEndRow(i - 1); } } else { text = row.getCell(index).getStringCellValue(); if (StringUtils.isNotEmpty(text)) { hanlderMergeCells(index, i, text, mergeDataMap, sheet, row.getCell(index), mergeMap.get(index)); } else { mergeCellOrContinue(index, mergeDataMap, sheet); } } } } if (mergeDataMap.size() > 0) { for (Integer index : mergeDataMap.keySet()) { //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B try{ sheet.addMergedRegion(new CellRangeAddress(mergeDataMap.get(index).getStartRow(), mergeDataMap.get(index).getEndRow(), index, index)); }catch (IllegalArgumentException e){ LOGGER.error("合并单元格错误日志:"+e.getMessage()); e.fillInStackTrace(); } //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B } } } /** * 处理合并单元格 * * @param index * @param rowNum * @param text * @param mergeDataMap * @param sheet * @param cell * @param delys */ private static void hanlderMergeCells(Integer index, int rowNum, String text, Map mergeDataMap, Sheet sheet, Cell cell, int[] delys) { if (mergeDataMap.containsKey(index)) { if (checkIsEqualByCellContents(mergeDataMap.get(index), text, cell, delys, rowNum)) { mergeDataMap.get(index).setEndRow(rowNum); } else { //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B try{ sheet.addMergedRegion(new CellRangeAddress(mergeDataMap.get(index).getStartRow(), mergeDataMap.get(index).getEndRow(), index, index)); }catch (IllegalArgumentException e){ LOGGER.error("合并单元格错误日志:"+e.getMessage()); e.fillInStackTrace(); } //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B mergeDataMap.put(index, createMergeEntity(text, rowNum, cell, delys)); } } else { mergeDataMap.put(index, createMergeEntity(text, rowNum, cell, delys)); } } /** * 字符为空的情况下判断 * * @param index * @param mergeDataMap * @param sheet */ private static void mergeCellOrContinue(Integer index, Map mergeDataMap, Sheet sheet) { if (mergeDataMap.containsKey(index) && mergeDataMap.get(index).getEndRow() != mergeDataMap.get(index).getStartRow()) { //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B try{ sheet.addMergedRegion(new CellRangeAddress(mergeDataMap.get(index).getStartRow(), mergeDataMap.get(index).getEndRow(), index, index)); }catch (IllegalArgumentException e){ LOGGER.error("合并单元格错误日志:"+e.getMessage()); e.fillInStackTrace(); } //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B mergeDataMap.remove(index); } } private static MergeEntity createMergeEntity(String text, int rowNum, Cell cell, int[] delys) { MergeEntity mergeEntity = new MergeEntity(text, rowNum, rowNum); // 存在依赖关系 if (delys != null && delys.length != 0) { List list = new ArrayList(delys.length); mergeEntity.setRelyList(list); for (int i = 0; i < delys.length; i++) { list.add(getCellNotNullText(cell, delys[i], rowNum)); } } return mergeEntity; } private static boolean checkIsEqualByCellContents(MergeEntity mergeEntity, String text, Cell cell, int[] delys, int rowNum) { // 没有依赖关系 if (delys == null || delys.length == 0) { return mergeEntity.getText().equals(text); } // 存在依赖关系 if (mergeEntity.getText().equals(text)) { for (int i = 0; i < delys.length; i++) { if (!getCellNotNullText(cell, delys[i], rowNum).equals(mergeEntity.getRelyList().get(i))) { return false; } } return true; } return false; } /** * 获取一个单元格的值,确保这个单元格必须有值,不然向上查询 * * @param cell * @param index * @param rowNum * @return */ private static String getCellNotNullText(Cell cell, int index, int rowNum) { String temp = cell.getRow().getCell(index).getStringCellValue(); while (StringUtils.isEmpty(temp)) { temp = cell.getRow().getSheet().getRow(--rowNum).getCell(index).getStringCellValue(); } return temp; } //update-begin---author:liusq Date:20220104 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- /** *处理合并的单元格区域 * @param sheet * @param firstRow 开始行 * @param lastRow 结束行 * @param firstCol 开始列 * @param lastCol 结束列 * @date 2022年1月4号 */ public static void addMergedRegion(Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) { try { //添加合并的单元格区域 sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol)); } catch (Exception e) { LOGGER.debug("发生了一次合并单元格错误,{},{},{},{}", new Integer[]{ firstRow, lastRow, firstCol, lastCol }); // 忽略掉合并的错误,不打印异常 LOGGER.debug(e.getMessage(), e); } } //update-end---author:liusq Date:20220104 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/PoiPublicUtil.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.util; import java.awt.image.BufferedImage; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.math.BigDecimal; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.imageio.ImageIO; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFClientAnchor; import org.apache.poi.hssf.usermodel.HSSFPicture; import org.apache.poi.hssf.usermodel.HSSFPictureData; import org.apache.poi.hssf.usermodel.HSSFShape; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ooxml.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePartName; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.*; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.jeecgframework.poi.excel.annotation.Excel; import org.jeecgframework.poi.excel.annotation.ExcelCollection; import org.jeecgframework.poi.excel.annotation.ExcelEntity; import org.jeecgframework.poi.excel.annotation.ExcelIgnore; import org.jeecgframework.poi.excel.entity.vo.PoiBaseConstants; import org.jeecgframework.poi.word.entity.WordImageEntity; import org.jeecgframework.poi.word.entity.params.ExcelListEntity; import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ClassUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * AutoPoi 的公共基础类 * * @author JEECG * @date 2015年4月5日 上午12:59:22 */ public final class PoiPublicUtil { private static final Logger LOGGER = LoggerFactory.getLogger(PoiPublicUtil.class); private PoiPublicUtil() { } @SuppressWarnings({ "unchecked" }) public static Map mapFor(Object... mapping) { Map map = new HashMap(); for (int i = 0; i < mapping.length; i += 2) { map.put((K) mapping[i], (V) mapping[i + 1]); } return map; } /** * 彻底创建一个对象 * * @param clazz * @return */ public static Object createObject(Class clazz, String targetId) { Object obj = null; Method setMethod; try { if (clazz.equals(Map.class)) { return new LinkedHashMap(); } obj = clazz.newInstance(); Field[] fields = getClassFields(clazz); for (Field field : fields) { if (isNotUserExcelUserThis(null, field, targetId)) { continue; } if (isCollection(field.getType())) { ExcelCollection collection = field.getAnnotation(ExcelCollection.class); setMethod = getMethod(field.getName(), clazz, field.getType()); setMethod.invoke(obj, collection.type().newInstance()); } else if (!isJavaClass(field)) { setMethod = getMethod(field.getName(), clazz, field.getType()); setMethod.invoke(obj, createObject(field.getType(), targetId)); } } } catch (Exception e) { LOGGER.error(e.getMessage(), e); throw new RuntimeException("创建对象异常"); } return obj; } /** * 获取class的 包括父类的 * * @param clazz * @return */ public static Field[] getClassFields(Class clazz) { List list = new ArrayList(); Field[] fields; do { fields = clazz.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { list.add(fields[i]); } clazz = clazz.getSuperclass(); } while (clazz != Object.class && clazz != null); return list.toArray(fields); } /** * @param photoByte * @return */ public static String getFileExtendName(byte[] photoByte) { String strFileExtendName = "JPG"; if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) { strFileExtendName = "GIF"; } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) { strFileExtendName = "JPG"; } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) { strFileExtendName = "BMP"; } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) { strFileExtendName = "PNG"; } return strFileExtendName; } /** * 获取GET方法 * * @param name * @param pojoClass * @return * @throws Exception */ public static Method getMethod(String name, Class pojoClass) throws Exception { StringBuffer getMethodName = new StringBuffer(PoiBaseConstants.GET); getMethodName.append(name.substring(0, 1).toUpperCase()); getMethodName.append(name.substring(1)); Method method = null; try { method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); } catch (Exception e) { method = pojoClass.getMethod(getMethodName.toString().replace(PoiBaseConstants.GET, PoiBaseConstants.IS), new Class[] {}); } return method; } /** * 获取SET方法 * * @param name * @param pojoClass * @param type * @return * @throws Exception */ public static Method getMethod(String name, Class pojoClass, Class type) throws Exception { StringBuffer getMethodName = new StringBuffer(PoiBaseConstants.SET); getMethodName.append(name.substring(0, 1).toUpperCase()); getMethodName.append(name.substring(1)); return pojoClass.getMethod(getMethodName.toString(), new Class[] { type }); } //update-begin-author:taoyan date:20180615 for:TASK #2798 导入扩展方法,支持自定义导入字段转换规则 /** * 获取get方法 通过EXCEL注解exportConvert判断是否支持值的转换 * @param name * @param pojoClass * @param convert * @return * @throws Exception */ public static Method getMethod(String name, Class pojoClass,boolean convert) throws Exception { StringBuffer getMethodName = new StringBuffer(); if(convert){ getMethodName.append(PoiBaseConstants.CONVERT); } getMethodName.append(PoiBaseConstants.GET); getMethodName.append(name.substring(0, 1).toUpperCase()); getMethodName.append(name.substring(1)); Method method = null; try { method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); } catch (Exception e) { method = pojoClass.getMethod(getMethodName.toString().replace(PoiBaseConstants.GET, PoiBaseConstants.IS), new Class[] {}); } return method; } /** * 获取set方法 通过EXCEL注解importConvert判断是否支持值的转换 * @param name * @param pojoClass * @param type * @param convert * @return * @throws Exception */ public static Method getMethod(String name, Class pojoClass, Class type,boolean convert) throws Exception { StringBuffer setMethodName = new StringBuffer(); if(convert){ setMethodName.append(PoiBaseConstants.CONVERT); } setMethodName.append(PoiBaseConstants.SET); setMethodName.append(name.substring(0, 1).toUpperCase()); setMethodName.append(name.substring(1)); return pojoClass.getMethod(setMethodName.toString(), new Class[] { type }); } //update-end-author:taoyan date:20180615 for:TASK #2798 导入扩展方法,支持自定义导入字段转换规则 /** * 获取Excel2003图片 * * @param sheet * 当前sheet对象 * @param workbook * 工作簿对象 * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData */ public static Map getSheetPictrues03(HSSFSheet sheet, HSSFWorkbook workbook) { Map sheetIndexPicMap = new HashMap(); List pictures = workbook.getAllPictures(); if (!pictures.isEmpty()) { for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) { HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor(); if (shape instanceof HSSFPicture) { HSSFPicture pic = (HSSFPicture) shape; int pictureIndex = pic.getPictureIndex() - 1; HSSFPictureData picData = pictures.get(pictureIndex); String picIndex = String.valueOf(anchor.getRow1()) + "_" + String.valueOf(anchor.getCol1()); sheetIndexPicMap.put(picIndex, picData); } } return sheetIndexPicMap; } else { return null; } } /** * 获取Excel2007图片 * * @param sheet * 当前sheet对象 * @param workbook * 工作簿对象 * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData */ public static Map getSheetPictrues07(XSSFSheet sheet, XSSFWorkbook workbook) { Map sheetIndexPicMap = new HashMap(); for (POIXMLDocumentPart dr : sheet.getRelations()) { if (dr instanceof XSSFDrawing) { XSSFDrawing drawing = (XSSFDrawing) dr; List shapes = drawing.getShapes(); for (XSSFShape shape : shapes) { XSSFPicture pic = (XSSFPicture) shape; XSSFClientAnchor anchor = pic.getPreferredSize(); CTMarker ctMarker = anchor.getFrom(); String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); sheetIndexPicMap.put(picIndex, pic.getPictureData()); } } } return sheetIndexPicMap; } /** * * 获取嵌入图片
* 支持excel2007+版本. * @param sheet * @param isCopy * @param book * @return * @author chenrui * @date 2024/4/2 20:25 */ public static Map getCellImages(Sheet sheet, ByteArrayOutputStream isCopy,Workbook book) { // 获取所有嵌入图片的单元格的内容 date:2024/4/2 Map cellImageMap = new HashMap<>(); Iterator rows = sheet.rowIterator(); while (rows.hasNext()) { Row row = rows.next(); Iterator cells = row.cellIterator(); while (cells.hasNext()) { Cell cell = cells.next(); CellType cellType = cell.getCellType(); if(cellType.equals(CellType.FORMULA)) { CellType resultType = cell.getCachedFormulaResultType(); if(!resultType.equals(CellType.STRING)){ continue; } String cellVal = cell.getStringCellValue(); if (null != cellVal && cellVal.startsWith("=DISPIMG")) { int start = cellVal.indexOf("\""); int end = cellVal.lastIndexOf("\""); if (start != -1 && end != -1) { String imgId = cellVal.substring(start + 1, end); CellImage cellImage = new CellImage(); cellImage.setImgId(imgId); cellImage.setCellStr(cellVal); cellImageMap.put(row.getRowNum() + "_" + cell.getColumnIndex(), cellImage); } } } } } try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(isCopy.toByteArray())); ZipInputStream fzis = new ZipInputStream(new ByteArrayInputStream(isCopy.toByteArray()))) { //update-begin---author:chenrui ---date:20240407 for:[QQYUN-8898]不依赖hutool,xml解析改为dom------------ DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); ZipEntry entry; // 获取嵌入单元格图片的rid while ((entry = zis.getNextEntry()) != null) { try { final String fileName = entry.getName(); if (Objects.equals(fileName, "xl/cellimages.xml")) { String content = IOUtils.toString(zis, StandardCharsets.UTF_8); Document document = documentBuilder.parse(new InputSource(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)))); NodeList cellImages = document.getElementsByTagName("etc:cellImage"); if (Objects.isNull(cellImages)) { continue; } for (int i = 0; i < cellImages.getLength(); i++) { Node cellImageNode = cellImages.item(i); NodeList cNvPr = ((Element) cellImageNode).getElementsByTagName("xdr:cNvPr"); if(cNvPr.getLength()<1){ continue; } Node cNvPrNode = cNvPr.item(0); String name = ((Element) cNvPrNode).getAttribute("name"); if (StringUtils.isNotEmpty(name)) { CellImage tempCellimage = cellImageMap.values().stream().filter(item -> Objects.equals(item.getImgId(), name)).findFirst().orElse(null); if (Objects.nonNull(tempCellimage)) { NodeList blips = ((Element) cellImageNode).getElementsByTagName("a:blip"); if(blips.getLength()<1){ continue; } Node blip = blips.item(0); String embed =((Element) blip).getAttribute("r:embed"); if(embed.isEmpty()){ continue; } tempCellimage.setRId(embed); } } } } } catch (SAXException e) { throw new RuntimeException(e); } finally { zis.closeEntry(); } } // 获取嵌入单元格图片的存放位置 while ((entry = fzis.getNextEntry()) != null) { try { final String fileName = entry.getName(); if (Objects.equals(fileName, "xl/_rels/cellimages.xml.rels")) { String content = IOUtils.toString(fzis, StandardCharsets.UTF_8); Document document = documentBuilder.parse(new InputSource(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)))); NodeList relationships = document.getElementsByTagName("Relationship"); if (Objects.isNull(relationships)) { continue; } for (int i = 0; i < relationships.getLength(); i++) { Node relationshipNode = relationships.item(i); if(relationshipNode instanceof Element){ Element relationshipEl = (Element) relationshipNode; String id = relationshipEl.getAttribute("Id"); String target = "/xl/" + relationshipEl.getAttribute("Target"); if (StringUtils.isNotEmpty(id)) { List cellImages = cellImageMap.values().stream().filter(item -> Objects.equals(item.getRId(), id)).collect(Collectors.toList()); cellImages.stream().filter(Objects::nonNull).forEach(cellImage -> cellImage.setImgName(target)); } } } } } catch (SAXException e) { throw new RuntimeException(e); } finally { fzis.closeEntry(); } } // 获取嵌入单元格图片的图片数据 List allPictures = (List) book.getAllPictures(); for (XSSFPictureData pictureData : allPictures) { PackagePartName partName = pictureData.getPackagePart().getPartName(); URI uri = partName.getURI(); List cellImages = cellImageMap.values().stream().filter(i -> Objects.equals(i.getImgName(), uri.toString())).collect(Collectors.toList()); cellImages.stream().filter(Objects::nonNull).forEach(cellImage -> cellImage.setPictureData(pictureData)); } //update-end---author:chenrui ---date:20240407 for:[QQYUN-8898]不依赖hutool,xml解析改为dom------------ } catch (IOException | ParserConfigurationException e) { throw new RuntimeException(e); } Map resp = new HashMap<>(); if (!cellImageMap.isEmpty()) { cellImageMap.forEach((key, cellImage) -> { resp.put(key, cellImage.getPictureData()); }); } return resp; } /** * 嵌入单元格图片对象 * * @author chenrui * @date 2024/4/3 18:27 */ static class CellImage { /** * 图片id */ private String imgId; /** * 单元格内容 */ private String cellStr; /** * RId */ private String rId; /** * 图片名称 */ private String imgName; /** * 图片对象 */ private XSSFPictureData pictureData; public String getImgId() { return imgId; } public void setImgId(String imgId) { this.imgId = imgId; } public String getCellStr() { return cellStr; } public void setCellStr(String cellStr) { this.cellStr = cellStr; } public String getRId() { return rId; } public void setRId(String rId) { this.rId = rId; } public String getImgName() { return imgName; } public void setImgName(String imgName) { this.imgName = imgName; } public XSSFPictureData getPictureData() { return pictureData; } public void setPictureData(XSSFPictureData pictureData) { this.pictureData = pictureData; } } public static String getWebRootPath(String filePath) { try { String path = null; try { path = PoiPublicUtil.class.getClassLoader().getResource("").toURI().getPath(); } catch (URISyntaxException e) { //e.printStackTrace(); //update-begin-author:taoyan date:20211116 for: JAR包分离 发布出空指针 https://gitee.com/jeecg/jeecg-boot/issues/I4CMHK }catch (NullPointerException e) { path = PoiPublicUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath(); } //update-end-author:taoyan date:20211116 for: JAR包分离 发布出空指针 https://gitee.com/jeecg/jeecg-boot/issues/I4CMHK //update-begin--Author:zhangdaihao Date:20190424 for:解决springboot 启动模式,上传路径获取为空问题--------------------- if (path == null || path == "") { //解决springboot 启动模式,上传路径获取为空问题 path = ClassUtils.getDefaultClassLoader().getResource("").getPath(); } //update-end--Author:zhangdaihao Date:20190424 for:解决springboot 启动模式,上传路径获取为空问题---------------------- LOGGER.debug("--- getWebRootPath ----filePath--- " + path); path = path.replace("WEB-INF/classes/", ""); path = path.replace("file:/", ""); LOGGER.debug("--- path--- " + path); LOGGER.debug("--- filePath--- " + filePath); return path + filePath; } catch (Exception e) { throw new RuntimeException(e); } } /** * 判断是不是集合的实现类 * * @param clazz * @return */ public static boolean isCollection(Class clazz) { return Collection.class.isAssignableFrom(clazz); } /** * 是不是java基础类 * * @param field * @return */ public static boolean isJavaClass(Field field) { Class fieldType = field.getType(); boolean isBaseClass = false; if (fieldType.isArray()) { isBaseClass = false; } else if (fieldType.isPrimitive() || fieldType.getPackage() == null || fieldType.getPackage().getName().equals("java.lang") || fieldType.getPackage().getName().equals("java.math") || fieldType.getPackage().getName().equals("java.sql") || fieldType.getPackage().getName().equals("java.util") || fieldType.getPackage().getName().equals("java.time")) { isBaseClass = true; } return isBaseClass; } /** * 判断是否不要在这个excel操作中 * * @param * @param field * @param targetId * @return */ public static boolean isNotUserExcelUserThis(List exclusionsList, Field field, String targetId) { boolean boo = true; if (field.getAnnotation(ExcelIgnore.class) != null) { boo = true; } else if (boo && field.getAnnotation(ExcelCollection.class) != null && isUseInThis(field.getAnnotation(ExcelCollection.class).name(), targetId) && (exclusionsList == null || !exclusionsList.contains(field.getAnnotation(ExcelCollection.class).name()))) { boo = false; } else if (boo && field.getAnnotation(Excel.class) != null && isUseInThis(field.getAnnotation(Excel.class).name(), targetId) && (exclusionsList == null || !exclusionsList.contains(field.getAnnotation(Excel.class).name()))) { boo = false; } else if (boo && field.getAnnotation(ExcelEntity.class) != null && isUseInThis(field.getAnnotation(ExcelEntity.class).name(), targetId) && (exclusionsList == null || !exclusionsList.contains(field.getAnnotation(ExcelEntity.class).name()))) { boo = false; } return boo; } /** * 判断是不是使用 * * @param exportName * @param targetId * @return */ private static boolean isUseInThis(String exportName, String targetId) { return targetId == null || exportName.equals("") || exportName.indexOf("_") < 0 || exportName.indexOf(targetId) != -1; } private static Integer getImageType(String type) { if (type.equalsIgnoreCase("JPG") || type.equalsIgnoreCase("JPEG")) { return XWPFDocument.PICTURE_TYPE_JPEG; } if (type.equalsIgnoreCase("GIF")) { return XWPFDocument.PICTURE_TYPE_GIF; } if (type.equalsIgnoreCase("BMP")) { return XWPFDocument.PICTURE_TYPE_GIF; } if (type.equalsIgnoreCase("PNG")) { return XWPFDocument.PICTURE_TYPE_PNG; } return XWPFDocument.PICTURE_TYPE_JPEG; } /** * 返回流和图片类型 * * @Author JEECG * @date 2013-11-20 * @param entity * @return (byte[]) isAndType[0],(Integer)isAndType[1] * @throws Exception */ public static Object[] getIsAndType(WordImageEntity entity) throws Exception { Object[] result = new Object[2]; String type; if (entity.getType().equals(WordImageEntity.URL)) { ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); BufferedImage bufferImg; String path = Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath() + entity.getUrl(); path = path.replace("WEB-INF/classes/", ""); path = path.replace("file:/", ""); bufferImg = ImageIO.read(new File(path)); //update-begin-author:taoYan date:20211203 for: Excel 导出图片的文件带小数点符号 导出报错 https://gitee.com/jeecg/jeecg-boot/issues/I4JNHR ImageIO.write(bufferImg, entity.getUrl().substring(entity.getUrl().lastIndexOf(".") + 1, entity.getUrl().length()), byteArrayOut); //update-end-author:taoYan date:20211203 for: Excel 导出图片的文件带小数点符号 导出报错 https://gitee.com/jeecg/jeecg-boot/issues/I4JNHR result[0] = byteArrayOut.toByteArray(); type = entity.getUrl().split("/.")[entity.getUrl().split("/.").length - 1]; } else { result[0] = entity.getData(); type = PoiPublicUtil.getFileExtendName(entity.getData()); } result[1] = getImageType(type); return result; } /** * 获取参数值 * * @param params * @param map * @return */ @SuppressWarnings("rawtypes") public static Object getParamsValue(String params, Object object) throws Exception { if (params.indexOf(".") != -1) { String[] paramsArr = params.split("\\."); return getValueDoWhile(object, paramsArr, 0); } if (object instanceof Map) { return ((Map) object).get(params); } return getMethod(params, object.getClass()).invoke(object, new Object[] {}); } /** * 解析数据 * * @Author JEECG * @date 2013-11-16 * @return */ public static Object getRealValue(String currentText, Map map) throws Exception { String params = ""; while (currentText.indexOf("{{") != -1) { params = currentText.substring(currentText.indexOf("{{") + 2, currentText.indexOf("}}")); //update-begin-author:liusq---date:2024-08-07--for: [issues/6925]autopoi通过word模板生成word时:三目、求长、常量、日期转换没起效果 Object obj = PoiElUtil.eval(params.trim(), map); //update-end-author:liusq---date:2024-08-07--for: [issues/6925]autopoi通过word模板生成word时:三目、求长、常量、日期转换没起效果 // 判断图片或者是集合 // update-begin-author:taoyan date:20210914 for:autopoi模板导出,赋值的方法建议增加空判断或抛出异常说明。 /issues/3005 if(obj==null){ obj = ""; } // update-end-author:taoyan date:20210914 for:autopoi模板导出,赋值的方法建议增加空判断或抛出异常说明。/issues/3005 if (obj instanceof WordImageEntity || obj instanceof List || obj instanceof ExcelListEntity) { return obj; } else { currentText = currentText.replace("{{" + params + "}}", obj.toString()); } } return currentText; } /** * 通过遍历过去对象值 * * @param object * @param paramsArr * @param index * @return * @throws Exception */ @SuppressWarnings("rawtypes") public static Object getValueDoWhile(Object object, String[] paramsArr, int index) throws Exception { if (object == null) { return ""; } if (object instanceof WordImageEntity) { return object; } if (object instanceof Map) { object = ((Map) object).get(paramsArr[index]); } else { object = getMethod(paramsArr[index], object.getClass()).invoke(object, new Object[] {}); } return (index == paramsArr.length - 1) ? (object == null ? "" : object) : getValueDoWhile(object, paramsArr, ++index); } /** * double to String 防止科学计数法 * * @param value * @return */ public static String doubleToString(Double value) { String temp = value.toString(); if (temp.contains("E")) { BigDecimal bigDecimal = new BigDecimal(temp); temp = bigDecimal.toPlainString(); } //---update-begin-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------ return ExcelUtil.remove0Suffix(temp); //---update-end-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------ } /** * 判断是否是数值类型 * @param xclass * @return */ public static boolean isNumber(String xclass){ if(xclass==null){ return false; } String temp = xclass.toLowerCase(); if(temp.indexOf("int")>=0 || temp.indexOf("double")>=0 || temp.indexOf("decimal")>=0){ return true; } return false; } //update-begin---author:liusq Date:20211217 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- /** * 统一 key的获取规则 * @param key * @param targetId * @date 2022年1月4号 * @return */ public static String getValueByTargetId(String key, String targetId, String defalut) { if (StringUtils.isEmpty(targetId) || key.indexOf("_") < 0) { return key; } String[] arr = key.split(","); String[] tempArr; for (String str : arr) { tempArr = str.split("_"); if (tempArr == null || tempArr.length < 2) { return defalut; } if (targetId.equals(tempArr[1])) { return tempArr[0]; } } return defalut; } //update-end---author:liusq Date:20211217 for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】---- } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/PoiReflectorUtil.java ================================================ package org.jeecgframework.poi.util; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ReflectPermission; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * 反射工具类,缓存读取的class信息,省的一直获取 * @Description [LOWCOD-2521]【autopoi】大数据导出方法【全局】 * @author liusq * @date 2022年1月4号 */ public final class PoiReflectorUtil { private static final Map, PoiReflectorUtil> CACHE_REFLECTOR = new ConcurrentHashMap, PoiReflectorUtil>(); private Map getMethods = new HashMap(); private Map setMethods = new HashMap(); private Map enumMethods = new HashMap(); private List fieldList = new ArrayList(); private Class type; private PoiReflectorUtil(Class clazz) { this.type = clazz; addGetMethods(clazz); addFields(clazz); addSetMethods(clazz); } public static PoiReflectorUtil forClass(Class clazz) { return new PoiReflectorUtil(clazz); } public static PoiReflectorUtil fromCache(Class clazz) { if (!CACHE_REFLECTOR.containsKey(clazz)) { CACHE_REFLECTOR.put(clazz, new PoiReflectorUtil(clazz)); } return CACHE_REFLECTOR.get(clazz); } private void addGetMethods(Class cls) { Map> conflictingGetters = new HashMap>(); Method[] methods = getClassMethods(cls); for (Method method : methods) { String name = method.getName(); if (name.startsWith("get") && name.length() > 3) { if (method.getParameterTypes().length == 0) { name = methodToProperty(name); addMethodConflict(conflictingGetters, name, method); } } else if (name.startsWith("is") && name.length() > 2) { if (method.getParameterTypes().length == 0) { name = methodToProperty(name); addMethodConflict(conflictingGetters, name, method); } } } resolveGetterConflicts(conflictingGetters); } private void resolveGetterConflicts(Map> conflictingGetters) { for (String propName : conflictingGetters.keySet()) { List getters = conflictingGetters.get(propName); Iterator iterator = getters.iterator(); Method firstMethod = iterator.next(); if (getters.size() == 1) { addGetMethod(propName, firstMethod); } else { Method getter = firstMethod; Class getterType = firstMethod.getReturnType(); while (iterator.hasNext()) { Method method = iterator.next(); Class methodType = method.getReturnType(); if (methodType.equals(getterType)) { throw new RuntimeException( "Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + firstMethod.getDeclaringClass() + ". This breaks the JavaBeans " + "specification and can cause unpredicatble results."); } else if (methodType.isAssignableFrom(getterType)) { // OK getter type is descendant } else if (getterType.isAssignableFrom(methodType)) { getter = method; getterType = methodType; } else { throw new RuntimeException( "Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + firstMethod.getDeclaringClass() + ". This breaks the JavaBeans " + "specification and can cause unpredicatble results."); } } addGetMethod(propName, getter); } } } private void addGetMethod(String name, Method method) { if (isValidPropertyName(name)) { getMethods.put(name, method); } } private void addSetMethods(Class cls) { Map> conflictingSetters = new HashMap>(); Method[] methods = getClassMethods(cls); for (Method method : methods) { String name = method.getName(); if (name.startsWith("set") && name.length() > 3) { if (method.getParameterTypes().length == 1) { name = methodToProperty(name); addMethodConflict(conflictingSetters, name, method); } } } resolveSetterConflicts(conflictingSetters); } private static String methodToProperty(String name) { if (name.startsWith("is")) { name = name.substring(2); } else if (name.startsWith("get") || name.startsWith("set")) { name = name.substring(3); } else { throw new RuntimeException("Error parsing property name '" + name + "'. Didn't start with 'is', 'get' or 'set'."); } if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) { name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1); } return name; } private void addMethodConflict(Map> conflictingMethods, String name, Method method) { List list = conflictingMethods.get(name); if (list == null) { list = new ArrayList(); conflictingMethods.put(name, list); } list.add(method); } private void resolveSetterConflicts(Map> conflictingSetters) { for (String propName : conflictingSetters.keySet()) { List setters = conflictingSetters.get(propName); Method firstMethod = setters.get(0); if (setters.size() == 1) { addSetMethod(propName, firstMethod); } else { Iterator methods = setters.iterator(); Method setter = null; while (methods.hasNext()) { Method method = methods.next(); if (method.getParameterTypes().length == 1) { setter = method; break; } } if (setter == null) { throw new RuntimeException( "Illegal overloaded setter method with ambiguous type for property " + propName + " in class " + firstMethod.getDeclaringClass() + ". This breaks the JavaBeans " + "specification and can cause unpredicatble results."); } addSetMethod(propName, setter); } } } private void addSetMethod(String name, Method method) { if (isValidPropertyName(name)) { setMethods.put(name, method); } } private void addFields(Class clazz) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (canAccessPrivateMethods()) { try { field.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } if (field.isAccessible() && !"serialVersionUID".equalsIgnoreCase(field.getName())) { this.fieldList.add(field); } } if (clazz.getSuperclass() != null) { addFields(clazz.getSuperclass()); } } private boolean isValidPropertyName(String name) { return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name)); } /* * This method returns an array containing all methods * declared in this class and any superclass. * We use this method, instead of the simpler Class.getMethods(), * because we want to look for private methods as well. * * @param cls The class * @return An array containing all methods in this class */ private Method[] getClassMethods(Class cls) { HashMap uniqueMethods = new HashMap(); Class currentClass = cls; while (currentClass != null) { addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods()); // we also need to look for interface methods - // because the class may be abstract Class[] interfaces = currentClass.getInterfaces(); for (Class anInterface : interfaces) { addUniqueMethods(uniqueMethods, anInterface.getMethods()); } currentClass = currentClass.getSuperclass(); } Collection methods = uniqueMethods.values(); return methods.toArray(new Method[methods.size()]); } private void addUniqueMethods(HashMap uniqueMethods, Method[] methods) { for (Method currentMethod : methods) { if (!currentMethod.isBridge()) { String signature = getSignature(currentMethod); // check to see if the method is already known // if it is known, then an extended class must have // overridden a method if (!uniqueMethods.containsKey(signature)) { if (canAccessPrivateMethods()) { try { currentMethod.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } uniqueMethods.put(signature, currentMethod); } } } } private String getSignature(Method method) { StringBuilder sb = new StringBuilder(); Class returnType = method.getReturnType(); if (returnType != null) { sb.append(returnType.getName()).append('#'); } sb.append(method.getName()); Class[] parameters = method.getParameterTypes(); for (int i = 0; i < parameters.length; i++) { if (i == 0) { sb.append(':'); } else { sb.append(','); } sb.append(parameters[i].getName()); } return sb.toString(); } private boolean canAccessPrivateMethods() { try { SecurityManager securityManager = System.getSecurityManager(); if (null != securityManager) { securityManager.checkPermission(new ReflectPermission("suppressAccessChecks")); } } catch (SecurityException e) { return false; } return true; } public Method getGetMethod(String propertyName) { Method method = getMethods.get(propertyName); if (method == null) { throw new RuntimeException( "There is no getter for property named '" + propertyName + "' in '" + type + "'"); } return method; } public Method getSetMethod(String propertyName) { Method method = setMethods.get(propertyName); if (method == null) { throw new RuntimeException( "There is no setter for property named '" + propertyName + "' in '" + type + "'"); } return method; } /** * 获取field 值 * * @param obj * @param property * @return */ public Object getValue(Object obj, String property) { Object value = null; Method m = getMethods.get(property); if (m != null) { try { value = m.invoke(obj, new Object[]{}); } catch (Exception ex) { } } return value; } /** * 设置field值 * * @param obj 对象 * @param property * @param object 属性值 * @return */ public boolean setValue(Object obj, String property, Object object) { Method m = setMethods.get(property); if (m != null) { try { m.invoke(obj, object); return true; } catch (Exception ex) { return false; } } return false; } public Map getGetMethods() { return getMethods; } public List getFieldList() { return fieldList; } public Object execEnumStaticMethod(String staticMethod, Object params) { if (!enumMethods.containsKey(setMethods)) { try { enumMethods.put(staticMethod,type.getMethod(staticMethod,params.getClass())); } catch (NoSuchMethodException e) { throw new RuntimeException( "There is no enum for property named '" + staticMethod + "' in '" + type + "'"); } } try { return enumMethods.get(staticMethod).invoke(null,params); } catch (Exception e) { throw new RuntimeException(e); } } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/util/PoiSheetUtility.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.util; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; /** * 国外高手的,不过也不好,慎用,效率不行 Helper functions to aid in the management of sheets */ public class PoiSheetUtility extends Object { /** * Given a sheet, this method deletes a column from a sheet and moves all * the columns to the right of it to the left one cell. * * Note, this method will not update any formula references. * * @param sheet * @param column */ public static void deleteColumn(Sheet sheet, int columnToDelete) { int maxColumn = 0; for (int r = 0; r < sheet.getLastRowNum() + 1; r++) { Row row = sheet.getRow(r); // if no row exists here; then nothing to do; next! if (row == null) continue; // if the row doesn't have this many columns then we are good; next! int lastColumn = row.getLastCellNum(); if (lastColumn > maxColumn) maxColumn = lastColumn; if (lastColumn < columnToDelete) continue; for (int x = columnToDelete + 1; x < lastColumn + 1; x++) { Cell oldCell = row.getCell(x - 1); if (oldCell != null) row.removeCell(oldCell); Cell nextCell = row.getCell(x); if (nextCell != null) { Cell newCell = row.createCell(x - 1, nextCell.getCellType()); cloneCell(newCell, nextCell); } } } // Adjust the column widths for (int c = 0; c < maxColumn; c++) { sheet.setColumnWidth(c, sheet.getColumnWidth(c + 1)); } } /* * Takes an existing Cell and merges all the styles and forumla into the new * one */ private static void cloneCell(Cell cNew, Cell cOld) { cNew.setCellComment(cOld.getCellComment()); cNew.setCellStyle(cOld.getCellStyle()); switch (cNew.getCellType()) { case BOOLEAN: { cNew.setCellValue(cOld.getBooleanCellValue()); break; } case NUMERIC: { cNew.setCellValue(cOld.getNumericCellValue()); break; } case STRING: { cNew.setCellValue(cOld.getStringCellValue()); break; } case ERROR: { cNew.setCellValue(cOld.getErrorCellValue()); break; } case FORMULA: { cNew.setCellFormula(cOld.getCellFormula()); break; } } } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/word/WordExportUtil.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.word; import java.util.Map; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.jeecgframework.poi.word.parse.ParseWord07; /** * Word使用模板导出工具类 * * @author JEECG * @date 2013-11-16 * @version 1.0 */ public final class WordExportUtil { private WordExportUtil() { } /** * 解析Word2007版本 * * @param url * 模板地址 * @param map * 解析数据源 * @return */ public static XWPFDocument exportWord07(String url, Map map) throws Exception { return new ParseWord07().parseWord(url, map); } /** * 解析Word2007版本 * * @param XWPFDocument * 模板 * @param map * 解析数据源 * @return */ public static void exportWord07(XWPFDocument document, Map map) throws Exception { new ParseWord07().parseWord(document, map); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/word/entity/MyXWPFDocument.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.word.entity; import java.io.InputStream; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlToken; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 扩充document,修复图片插入失败问题问题 * * @author JEECG * @date 2013-11-20 * @version 1.0 */ public class MyXWPFDocument extends XWPFDocument { private static final Logger LOGGER = LoggerFactory.getLogger(MyXWPFDocument.class); private static String PICXML = "" + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; public MyXWPFDocument() { super(); } public MyXWPFDocument(InputStream in) throws Exception { super(in); } public MyXWPFDocument(OPCPackage opcPackage) throws Exception { super(opcPackage); } public void createPicture(String blipId, int id, int width, int height) { final int EMU = 9525; width *= EMU; height *= EMU; CTInline inline = createParagraph().createRun().getCTR().addNewDrawing().addNewInline(); String picXml = String.format(PICXML, id, blipId, width, height); XmlToken xmlToken = null; try { xmlToken = XmlToken.Factory.parse(picXml); } catch (XmlException xe) { LOGGER.error(xe.getMessage(), xe.fillInStackTrace()); } inline.set(xmlToken); inline.setDistT(0); inline.setDistB(0); inline.setDistL(0); inline.setDistR(0); CTPositiveSize2D extent = inline.addNewExtent(); extent.setCx(width); extent.setCy(height); CTNonVisualDrawingProps docPr = inline.addNewDocPr(); docPr.setId(id); docPr.setName("Picture " + id); docPr.setDescr("Generated"); } public void createPicture(XWPFRun run, String blipId, int id, int width, int height) { final int EMU = 9525; width *= EMU; height *= EMU; CTInline inline = run.getCTR().addNewDrawing().addNewInline(); String picXml = String.format(PICXML, id, blipId, width, height); XmlToken xmlToken = null; try { xmlToken = XmlToken.Factory.parse(picXml); } catch (XmlException xe) { LOGGER.error(xe.getMessage(), xe.fillInStackTrace()); } inline.set(xmlToken); inline.setDistT(0); inline.setDistB(0); inline.setDistL(0); inline.setDistR(0); CTPositiveSize2D extent = inline.addNewExtent(); extent.setCx(width); extent.setCy(height); CTNonVisualDrawingProps docPr = inline.addNewDocPr(); docPr.setId(id); docPr.setName("Picture " + id); docPr.setDescr("Generated"); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/word/entity/WordImageEntity.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.word.entity; /** * word导出,图片设置和图片信息 * * @author JEECG * @date 2013-11-17 * @version 1.0 */ public class WordImageEntity { public static String URL = "url"; public static String Data = "data"; /** * 图片输入方式 */ private String type = URL; /** * 图片宽度 */ private int width; // 图片高度 private int height; // 图片地址 private String url; // 图片信息 private byte[] data; public WordImageEntity() { } public WordImageEntity(byte[] data, int width, int height) { this.data = data; this.width = width; this.height = height; this.type = Data; } public WordImageEntity(String url, int width, int height) { this.url = url; this.width = width; this.height = height; } public byte[] getData() { return data; } public int getHeight() { return height; } public String getType() { return type; } public String getUrl() { return url; } public int getWidth() { return width; } public void setData(byte[] data) { this.data = data; } public void setHeight(int height) { this.height = height; } public void setType(String type) { this.type = type; } public void setUrl(String url) { this.url = url; } public void setWidth(int width) { this.width = width; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/word/entity/params/ExcelListEntity.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.word.entity.params; import java.util.List; import org.jeecgframework.poi.excel.entity.ExcelBaseParams; import org.jeecgframework.poi.handler.inter.IExcelDataHandler; /** * Excel 导出对象 * * @author JEECG * @date 2014年8月9日 下午10:21:13 */ public class ExcelListEntity extends ExcelBaseParams { /** * 数据源 */ private List list; /** * 实体类对象 */ private Class clazz; /** * 表头行数 */ private int headRows = 1; public ExcelListEntity() { } public ExcelListEntity(List list, Class clazz) { this.list = list; this.clazz = clazz; } public ExcelListEntity(List list, Class clazz, IExcelDataHandler dataHanlder) { this.list = list; this.clazz = clazz; setDataHanlder(dataHanlder); } public ExcelListEntity(List list, Class clazz, IExcelDataHandler dataHanlder, int headRows) { this.list = list; this.clazz = clazz; this.headRows = headRows; setDataHanlder(dataHanlder); } public ExcelListEntity(List list, Class clazz, int headRows) { this.list = list; this.clazz = clazz; this.headRows = headRows; } public Class getClazz() { return clazz; } public int getHeadRows() { return headRows; } public List getList() { return list; } public void setClazz(Class clazz) { this.clazz = clazz; } public void setHeadRows(int headRows) { this.headRows = headRows; } public void setList(List list) { this.list = list; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/word/entity/params/ListParamEntity.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.word.entity.params; /** * Excel 对象导出结构 * * @author JEECG * @date 2014年7月26日 下午11:14:48 */ public class ListParamEntity { // 唯一值,在遍历中重复使用 public static final String SINGLE = "single"; // 属于数组类型 public static final String LIST = "list"; /** * 属性名称 */ private String name; /** * 目标 */ private String target; /** * 当是唯一值的时候直接求出值 */ private Object value; /** * 数据类型,SINGLE || LIST */ private String type; public ListParamEntity() { } public ListParamEntity(String name, Object value) { this.name = name; this.value = value; this.type = LIST; } public ListParamEntity(String name, String target) { this.name = name; this.target = target; this.type = LIST; } public String getName() { return name; } public String getTarget() { return target; } public String getType() { return type; } public Object getValue() { return value; } public void setName(String name) { this.name = name; } public void setTarget(String target) { this.target = target; } public void setType(String type) { this.type = type; } public void setValue(Object value) { this.value = value; } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/word/parse/ParseWord07.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.word.parse; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFFooter; import org.apache.poi.xwpf.usermodel.XWPFHeader; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.jeecgframework.poi.cache.WordCache; import org.jeecgframework.poi.util.PoiPublicUtil; import org.jeecgframework.poi.word.entity.MyXWPFDocument; import org.jeecgframework.poi.word.entity.WordImageEntity; import org.jeecgframework.poi.word.entity.params.ExcelListEntity; import org.jeecgframework.poi.word.parse.excel.ExcelEntityParse; import org.jeecgframework.poi.word.parse.excel.ExcelMapParse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 解析07版的Word,替换文字,生成表格,生成图片 * * @author JEECG * @date 2013-11-16 * @version 1.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class ParseWord07 { private static final Logger LOGGER = LoggerFactory.getLogger(ParseWord07.class); /** * 添加图片 * * @Author JEECG * @date 2013-11-20 * @param obj * @param currentRun * @throws Exception */ private void addAnImage(WordImageEntity obj, XWPFRun currentRun) throws Exception { Object[] isAndType = PoiPublicUtil.getIsAndType(obj); String picId; try { picId = currentRun.getParagraph().getDocument().addPictureData((byte[]) isAndType[0], (Integer) isAndType[1]); ((MyXWPFDocument) currentRun.getParagraph().getDocument()).createPicture(currentRun, picId, currentRun.getParagraph().getDocument().getNextPicNameNumber((Integer) isAndType[1]), obj.getWidth(), obj.getHeight()); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } /** * 根据条件改变值 * * @param map * @Author JEECG * @date 2013-11-16 */ private void changeValues(XWPFParagraph paragraph, XWPFRun currentRun, String currentText, List runIndex, Map map) throws Exception { Object obj = PoiPublicUtil.getRealValue(currentText, map); if (obj instanceof WordImageEntity) {// 如果是图片就设置为图片 currentRun.setText("", 0); addAnImage((WordImageEntity) obj, currentRun); } else { currentText = obj.toString(); currentRun.setText(currentText, 0); } for (int k = 0; k < runIndex.size(); k++) { paragraph.getRuns().get(runIndex.get(k)).setText("", 0); } runIndex.clear(); } /** * 判断是不是迭代输出 * * @Author JEECG * @date 2013-11-18 * @return * @throws Exception */ private Object checkThisTableIsNeedIterator(XWPFTableCell cell, Map map) throws Exception { String text = cell.getText().trim(); //begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】-------- // 判断是不是迭代输出 if (text != null&&text.startsWith("{{") && text.indexOf("$fe:") != -1) { return PoiPublicUtil.getRealValue(text.replace("$fe:", "").trim(), map); } //end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】-------- return null; } /** * 解析所有的文本 * * @Author JEECG * @date 2013-11-17 * @param paragraphs * @param map */ private void parseAllParagraphic(List paragraphs, Map map) throws Exception { XWPFParagraph paragraph; for (int i = 0; i < paragraphs.size(); i++) { paragraph = paragraphs.get(i); if (paragraph.getText().indexOf("{{") != -1) { parseThisParagraph(paragraph, map); } } } /** * 解析这个段落 * * @Author JEECG * @date 2013-11-16 * @param paragraph * @param map */ private void parseThisParagraph(XWPFParagraph paragraph, Map map) throws Exception { XWPFRun run; XWPFRun currentRun = null;// 拿到的第一个run,用来set值,可以保存格式 String currentText = "";// 存放当前的text String text; Boolean isfinde = false;// 判断是不是已经遇到{{ List runIndex = new ArrayList();// 存储遇到的run,把他们置空 for (int i = 0; i < paragraph.getRuns().size(); i++) { run = paragraph.getRuns().get(i); text = run.getText(0); if (StringUtils.isEmpty(text)) { continue; }// 如果为空或者""这种这继续循环跳过 if (isfinde) { currentText += text; if (currentText.indexOf("{{") == -1) { isfinde = false; runIndex.clear(); } else { runIndex.add(i); } if (currentText.indexOf("}}") != -1) { changeValues(paragraph, currentRun, currentText, runIndex, map); currentText = ""; isfinde = false; } } else if (text.indexOf("{") >= 0) {// 判断是不是开始 currentText = text; isfinde = true; currentRun = run; } else { currentText = ""; } if (currentText.indexOf("}}") != -1) { changeValues(paragraph, currentRun, currentText, runIndex, map); isfinde = false; } } } private void parseThisRow(List cells, Map map) throws Exception { for (XWPFTableCell cell : cells) { parseAllParagraphic(cell.getParagraphs(), map); } } /** * 解析这个表格 * * @Author JEECG * @date 2013-11-17 * @param table * @param map */ private void parseThisTable(XWPFTable table, Map map) throws Exception { XWPFTableRow row; List cells; Object listobj; for (int i = 0; i < table.getNumberOfRows(); i++) { row = table.getRow(i); cells = row.getTableCells(); //begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】-------- listobj = checkThisTableIsNeedIterator(cells.get(0), map); if (listobj == null) { parseThisRow(cells, map); } else if (listobj instanceof ExcelListEntity) { new ExcelEntityParse().parseNextRowAndAddRow(table, i, (ExcelListEntity) listobj); i = i + ((ExcelListEntity) listobj).getList().size() - 1;//删除之后要往上挪一行,然后加上跳过新建的行数 } else { ExcelMapParse.parseNextRowAndAddRow(table, i, (List) listobj); i = i + ((List) listobj).size() - 1;//删除之后要往上挪一行,然后加上跳过新建的行数 } /*if (cells.size() == 1) { listobj = checkThisTableIsNeedIterator(cells.get(0), map); if (listobj == null) { parseThisRow(cells, map); } else if (listobj instanceof ExcelListEntity) { table.removeRow(i);// 删除这一行 excelEntityParse.parseNextRowAndAddRow(table, i, (ExcelListEntity) listobj); } else { table.removeRow(i);// 删除这一行 ExcelMapParse.parseNextRowAndAddRow(table, i, (List) listobj); } } else { parseThisRow(cells, map); }*/ //end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】-------- } } /** * 解析07版的Word并且进行赋值 * * @Author JEECG * @date 2013-11-16 * @return * @throws Exception */ public XWPFDocument parseWord(String url, Map map) throws Exception { MyXWPFDocument doc = WordCache.getXWPFDocumen(url); parseWordSetValue(doc, map); return doc; } /** * 解析07版的Word并且进行赋值 * * @Author JEECG * @date 2013-11-16 * @return * @throws Exception */ public void parseWord(XWPFDocument document, Map map) throws Exception { parseWordSetValue((MyXWPFDocument) document, map); } private void parseWordSetValue(MyXWPFDocument doc, Map map) throws Exception { // 第一步解析文档 parseAllParagraphic(doc.getParagraphs(), map); // 第二步解析页眉,页脚 parseHeaderAndFoot(doc, map); // 第三步解析所有表格 XWPFTable table; Iterator itTable = doc.getTablesIterator(); while (itTable.hasNext()) { table = itTable.next(); if (table.getText().indexOf("{{") != -1) { parseThisTable(table, map); } } } /** * 解析页眉和页脚 * * @param doc * @param map * @throws Exception */ private void parseHeaderAndFoot(MyXWPFDocument doc, Map map) throws Exception { List headerList = doc.getHeaderList(); for (XWPFHeader xwpfHeader : headerList) { for (int i = 0; i < xwpfHeader.getListParagraph().size(); i++) { parseThisParagraph(xwpfHeader.getListParagraph().get(i), map); } } List footerList = doc.getFooterList(); for (XWPFFooter xwpfFooter : footerList) { for (int i = 0; i < xwpfFooter.getListParagraph().size(); i++) { parseThisParagraph(xwpfFooter.getListParagraph().get(i), map); } } } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/word/parse/excel/ExcelEntityParse.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.word.parse.excel; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.jeecgframework.poi.excel.annotation.ExcelTarget; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.jeecgframework.poi.excel.export.base.ExportBase; import org.jeecgframework.poi.exception.word.WordExportException; import org.jeecgframework.poi.exception.word.enmus.WordExportEnum; import org.jeecgframework.poi.util.PoiPublicUtil; import org.jeecgframework.poi.word.entity.params.ExcelListEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 解析实体类对象 复用注解 * * @author JEECG * @date 2014年8月9日 下午10:30:57 */ public class ExcelEntityParse extends ExportBase { private static final Logger LOGGER = LoggerFactory.getLogger(ExcelEntityParse.class); private static void checkExcelParams(ExcelListEntity entity) { if (entity.getList() == null || entity.getClazz() == null) { throw new WordExportException(WordExportEnum.EXCEL_PARAMS_ERROR); } } private int createCells(int index, Object t, List excelParams, XWPFTable table, short rowHeight) throws Exception { ExcelExportEntity entity; XWPFTableRow row = table.createRow(); row.setHeight(rowHeight); int maxHeight = 1, cellNum = 0; for (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) { entity = excelParams.get(k); if (entity.getList() != null) { Collection list = (Collection) entity.getMethod().invoke(t, new Object[] {}); int listC = 0; for (Object obj : list) { createListCells(index + listC, cellNum, obj, entity.getList(), table); listC++; } cellNum += entity.getList().size(); if (list != null && list.size() > maxHeight) { maxHeight = list.size(); } } else { Object value = getCellValue(entity, t); if (entity.getType() == 1) { setCellValue(row, value, cellNum++); } } } // 合并需要合并的单元格 cellNum = 0; for (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) { entity = excelParams.get(k); if (entity.getList() != null) { cellNum += entity.getList().size(); } else if (entity.isNeedMerge()) { table.setCellMargins(index, index + maxHeight - 1, cellNum, cellNum); cellNum++; } } return maxHeight; } /** * 创建List之后的各个Cells * * @param styles */ public void createListCells(int index, int cellNum, Object obj, List excelParams, XWPFTable table) throws Exception { ExcelExportEntity entity; XWPFTableRow row; if (table.getRow(index) == null) { row = table.createRow(); row.setHeight(getRowHeight(excelParams)); } else { row = table.getRow(index); } for (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) { entity = excelParams.get(k); Object value = getCellValue(entity, obj); if (entity.getType() == 1) { setCellValue(row, value, cellNum++); } } } /** * 获取表头数据 * * @param table * @param index * @return */ private Map getTitleMap(XWPFTable table, int index, int headRows) { if (index < headRows) { throw new WordExportException(WordExportEnum.EXCEL_NO_HEAD); } Map map = new HashMap(); String text; for (int j = 0; j < headRows; j++) { List cells = table.getRow(index - j - 1).getTableCells(); for (int i = 0; i < cells.size(); i++) { text = cells.get(i).getText(); if (StringUtils.isEmpty(text)) { throw new WordExportException(WordExportEnum.EXCEL_HEAD_HAVA_NULL); } map.put(text, i); } } return map; } /** * 解析上一行并生成更多行 * * @param table * @param i * @param listobj */ public void parseNextRowAndAddRow(XWPFTable table, int index, ExcelListEntity entity) { checkExcelParams(entity); // 获取表头数据 Map titlemap = getTitleMap(table, index, entity.getHeadRows()); try { // 得到所有字段 Field fileds[] = PoiPublicUtil.getClassFields(entity.getClazz()); ExcelTarget etarget = entity.getClazz().getAnnotation(ExcelTarget.class); String targetId = null; if (etarget != null) { targetId = etarget.value(); } // 获取实体对象的导出数据 List excelParams = new ArrayList(); getAllExcelField(null, targetId, fileds, excelParams, entity.getClazz(), null); // 根据表头进行筛选排序 sortAndFilterExportField(excelParams, titlemap); short rowHeight = getRowHeight(excelParams); Iterator its = entity.getList().iterator(); while (its.hasNext()) { Object t = its.next(); index += createCells(index, t, excelParams, table, rowHeight); } } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } private void setCellValue(XWPFTableRow row, Object value, int cellNum) { if (row.getCell(cellNum++) != null) { row.getCell(cellNum - 1).setText(value == null ? "" : value.toString()); } else { row.createCell().setText(value == null ? "" : value.toString()); } } /** * 对导出序列进行排序和塞选 * * @param excelParams * @param titlemap * @return */ private void sortAndFilterExportField(List excelParams, Map titlemap) { for (int i = excelParams.size() - 1; i >= 0; i--) { if (excelParams.get(i).getList() != null && excelParams.get(i).getList().size() > 0) { sortAndFilterExportField(excelParams.get(i).getList(), titlemap); if (excelParams.get(i).getList().size() == 0) { excelParams.remove(i); } else { excelParams.get(i).setOrderNum(i); } } else { if (titlemap.containsKey(excelParams.get(i).getName())) { excelParams.get(i).setOrderNum(i); } else { excelParams.remove(i); } } } sortAllParams(excelParams); } } ================================================ FILE: autopoi/src/main/java/org/jeecgframework/poi/word/parse/excel/ExcelMapParse.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.word.parse.excel; import java.util.List; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.jeecgframework.poi.util.PoiPublicUtil; /** * 处理和生成Map 类型的数据变成表格 * * @author JEECG * @date 2014年8月9日 下午10:28:46 */ public final class ExcelMapParse { /** * 解析参数行,获取参数列表 * * @Author JEECG * @date 2013-11-18 * @param currentRow * @return */ private static String[] parseCurrentRowGetParams(XWPFTableRow currentRow) { List cells = currentRow.getTableCells(); String[] params = new String[cells.size()]; String text; for (int i = 0; i < cells.size(); i++) { text = cells.get(i).getText(); params[i] = text == null ? "" : text.trim().replace("{{", "").replace("}}", ""); } return params; } /** * 解析下一行,并且生成更多的行 * * @Author JEECG * @date 2013-11-18 * @param table * @param listobj2 */ public static void parseNextRowAndAddRow(XWPFTable table, int index, List list) throws Exception { XWPFTableRow currentRow = table.getRow(index); String[] params = parseCurrentRowGetParams(currentRow); table.removeRow(index);// 移除这一行 int cellIndex = 0;// 创建完成对象一行好像多了一个cell for (Object obj : list) { //begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】-------- currentRow = table.insertNewTableRow(index++); for (cellIndex = 0; cellIndex < currentRow.getTableCells().size(); cellIndex++) { String text = PoiPublicUtil.getValueDoWhile(obj, params[cellIndex].split("\\."), 0).toString(); currentRow.getTableCells().get(cellIndex).setText(text); } //end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------//-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】-------- for (; cellIndex < params.length; cellIndex++) { currentRow.createCell().setText(PoiPublicUtil.getValueDoWhile(obj, params[cellIndex].split("\\."), 0).toString()); } } } } ================================================ FILE: autopoi/src/test/java/DaoChuBigDataTest.java ================================================ import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.handler.inter.IExcelExportServer; import vo.TestEntity; import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @Description: 大数据量 Excel 导出测试 * 测试功能: * 1. 测试百万级数据的 Excel 导出性能 * 2. 测试分批次导出大数据,避免内存溢出 * 3. 验证 exportBigExcel 方法的实际应用效果 * * 技术要点: * - 使用 IExcelExportServer 接口实现分批数据加载 * - 每批次处理 10万条数据,避免一次性加载导致内存溢出 * - 适用于数据量超过 10万条的大数据导出场景 * * 参考文档: * - http://doc.wupaas.com/docs/easypoi/easypoi-1c10lbsojh62f * - https://blog.csdn.net/weixin_45214729/article/details/118552415 * * @author: liusq * @date: 2022年1月4日 */ public class DaoChuBigDataTest { // 生成文件的保存路径 private static final String generatePath = "D:/excel/"; /** * 大数据导出测试方法 * * 测试内容: * - 模拟生成 100万条测试数据 * - 使用分批导出方式,每批处理 10万条数据 * - 记录导出耗时,验证性能表现 * * 核心逻辑: * 1. 准备100万条测试数据(实际应用中应从数据库分批查询) * 2. 计算总页数(100万 / 10万 = 10页) * 3. 通过 IExcelExportServer 接口实现分批数据返回 * 4. 每次返回10万条数据,POI 自动写入 Excel * 5. 当返回 null 时,表示数据处理完成 * * 性能优化建议: * - 每批数据量建议控制在 1-10万条之间 * - 数据量过大(如30万/批)可能导致内存溢出 * - 实际应用中应从数据库分页查询,而不是一次性加载到内存 * * @throws Exception 文件操作或数据处理异常 */ public static void bigDataExport() throws Exception { Workbook workbook = null; List aList = new ArrayList(); ExportParams exportParams = new ExportParams(); Date start = new Date(); // 模拟100万条数据(实际应用中应该分批从数据库查询,避免内存溢出) System.out.println("开始准备100万条测试数据..."); for(int j=0;j<1000000;j++){ TestEntity testEntity = new TestEntity(); testEntity.setName("李四"+j); testEntity.setAge(j); aList.add(testEntity); } // 计算分页参数:总页数和每页大小 int totalPage = (aList.size() / 100000) + 1; // 总共10页 int pageSize = 100000; // 每页10万条数据 System.out.println("数据准备完成,开始分批导出,总页数:" + totalPage); /** * 使用 exportBigExcel 方法进行大数据导出 * * @param exportParams 导出参数配置 * @param TestEntity.class 数��实体类 * @param IExcelExportServer 数据服务接口,用于分批返回数据 * @param totalPage 总页数,传递给 selectListForExcelExport 方法作为终止条件 */ workbook = ExcelExportUtil.exportBigExcel(exportParams, TestEntity.class, new IExcelExportServer() { /** * 分批数据查询接口实现 * * 该方法会被多次调用,每次返回一批数据,直到返回 null 为止 * * @param obj 就是上面传入的 totalPage,用于控制循环终止条件 * @param page 当前页码(从1开始),每次自动+1 * @return 当前批次的数据列表,返回 null 表示数据处理完成 */ @Override public List selectListForExcelExport(Object obj, int page) { System.out.println("正在处理第 " + page + " 页数据..."); // 终止条件:当页码超过总页数时,返回 null if (page > totalPage) { return null; } // 计算当前批次的数据范围 int fromIndex = (page - 1) * pageSize; // 起始索引 int toIndex = page != totalPage ? fromIndex + pageSize : aList.size(); // 结束索引 // 返回当前批次的数据(使用 subList 截取) // 重要提示:实际应用中应该从数据库分页查询,而不是从内存中截取 List list = new ArrayList<>(); list.addAll(aList.subList(fromIndex, toIndex)); return list; } }, totalPage); // 保存文件 File savefile = new File(generatePath); if (!savefile.exists()) { savefile.mkdirs(); } FileOutputStream fos = new FileOutputStream("D:/excel/ExcelExportBigData.bigDataExport.xlsx"); workbook.write(fos); fos.close(); // 输出性能统计 long timeUsed = new Date().getTime() - start.getTime(); System.out.println("导出完成!耗时(秒):" + (timeUsed / 1000) + ",文件保存在:D:/excel/ExcelExportBigData.bigDataExport.xlsx"); } /** * 主方法:执行大数据导出测试 * * 预期结果: * - 在 D:/excel/ 目录下生成 ExcelExportBigData.bigDataExport.xlsx 文件 * - 文件包含100万行数据 * - 控制台输出各批次处理进度和总耗时 * * @param args 命令行参数 * @throws Exception 导出过程异常 */ public static void main(String[] args) throws Exception { bigDataExport(); } } ================================================ FILE: autopoi/src/test/java/DaoChuSheetTest.java ================================================ import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.entity.enmus.ExcelType; import vo.TestEntity; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Description: 多Sheet导出测试 * 测试功能: * 1. 测试一个 Workbook 中创建多个 Sheet 页签 * 2. 测试使用实体类(TestEntity)方式导出数据到不同 Sheet * 3. 测试 XSSF 格式(.xlsx)的多 Sheet 导出 * * 参考文档:http://doc.jeecg.com/2044223 * * @author: scott * @date: 2020年09月16日 11:46 */ public class DaoChuSheetTest { // 生成文件的保存路径 private static final String generatePath = "D:/excel/"; /** * 获取导出参数配置 * @param name 表格名称和Sheet名称 * @return ExportParams 配置对象,使用 XSSF 格式 */ public static ExportParams getExportParams(String name) { return new ExportParams(name,name,ExcelType.XSSF); } /** * 测试多Sheet导出功能 * * 测试内容: * - 创建3个Sheet页签 * - 每个Sheet使用相同的实体类(TestEntity) * - 每个Sheet包含10条测试数据 * - 验证多Sheet在同一个Excel文件中的导出效果 * * @return Workbook 对象,包含3个Sheet页签 */ public static Workbook test() { /** * 多个Map配置: * - title: 对应表格标题和Sheet名称(ExportParams对象) * - entity: 对应表格数据的实体类(如 TestEntity.class) * - data: 对应实际数据集合(Collection类型) * * 注意:也可以使用 Map 数据替代实体类,示例中 ls2 展示了这种方式 */ List> listMap = new ArrayList>(); // 创建3个Sheet for(int i=0;i<3;i++){ Map map = new HashMap(); map.put("title", getExportParams("测试"+i));//表格Title map.put("entity", TestEntity.class);//表格对应实体 // 方式1:使用实体类数据 List ls=new ArrayList (); for(int j=0;j<10;j++){ TestEntity testEntity = new TestEntity(); testEntity.setName("张三"+i+j); testEntity.setAge(18+i+j); ls.add(testEntity); } // 方式2:使用Map数据(示例代码,未使用) List ls2=new ArrayList (); for(int j=0;j<10;j++){ Map map1 = new HashMap(); map1.put("name","李四"+i+j); map1.put("age",18+i+j); ls2.add(map1); } map.put("data", ls);//可选:ls(实体类数据) or ls2(Map数据) listMap.add(map); } // 导出多个Sheet到同一个Workbook Workbook workbook = ExcelExportUtil.exportExcel(listMap, ExcelType.XSSF); return workbook; } /** * 主方法:执行测试并保存Excel文件 * * 执行步骤: * 1. 调用test()方法生成包含3个Sheet的Workbook * 2. 检查保存目录是否存在,不存在则创建 * 3. 将Workbook写入到文件 testSheet.xlsx * 4. 关闭文件输出流 * * 预期结果: * - 在 D:/excel/ 目录下生成 testSheet.xlsx 文件 * - 文件包含3个Sheet:测试0、测试1、测试2 * - 每个Sheet包含10行数据 * * @param args 命令行参数 * @throws IOException 文件写入异常 */ public static void main(String[] args) throws IOException { Workbook workbook = test(); File savefile = new File(generatePath); if (!savefile.exists()) { savefile.mkdirs(); } FileOutputStream fos = new FileOutputStream(generatePath + "testSheet.xlsx"); workbook.write(fos); fos.close(); } } ================================================ FILE: autopoi/src/test/java/DaoChuTest.java ================================================ import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.annotation.Excel; import org.jeecgframework.poi.excel.entity.TemplateExportParams; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.entity.enmus.ExcelType; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Description: Excel 模板导出测试 * 测试功能: * 1. 测试基于 Excel 模板文件的数据导出功能 * 2. 测试模板中的变量替换和列表数据循环填充 * 3. 验证模板导出在实际业务场景中的应用 * * 测试场景: * - 使用预定义的 Excel 模板(test.xlsx 或 testNextMarge.xlsx) * - 将 Map 数据填充到模板的占位符位置 * - 支持单个变量替换和列表数据的循环生成 * * @author: scott * @date: 2020年09月16日 11:46 */ public class DaoChuTest { // 模板文件路径:项目根目录/autopoi/src/test/resources/templates/ private static final String TEMPLATE_PATH = System.getProperty("user.dir") + File.separator + "autopoi" + File.separator + "src" + File.separator + "test" + File.separator + "resources" + File.separator + "templates" + File.separator; /** * 获取模板导出参数配置 * * @param name 模板文件名(不含扩展名),如 "test" 或 "testNextMarge" * @return TemplateExportParams 模板导出参数对象 */ public static TemplateExportParams getTemplateParams(String name) { return new TemplateExportParams(TEMPLATE_PATH + name + ".xlsx"); } /** * 测试模板导出功能 * * 测试内容: * - 加载指定名称的 Excel 模板文件 * - 构造测试数据(包含3条记录的列表) * - 将数据填充到模板的 autoList 循环区域 * - 生成包含实际数据的 Excel 文件 * * 数据结构: * - autoList: 列表数据,会在模板中循环生成多行 * - name: 姓名字段 * - isTts: 序号字段 * - sname: 简称字段 * - ttsContent: 内容字段 * - rate: 费率字段 * * @param name 模板文件名 * @return Workbook 填充数据后的工作簿对象 */ public static Workbook test(String name) { TemplateExportParams params = getTemplateParams(name); Map map = new HashMap(); // 构造列表数据(3条记录) List> listMap = new ArrayList>(); for (int i = 0; i < 3; i++) { Map lm = new HashMap(); lm.put("name", "姓名1" + i); lm.put("isTts", i); lm.put("sname", "s姓名"); lm.put("ttsContent", "ttsContent内容"); lm.put("rate", 1000 + i); listMap.add(lm); } // 将列表数据放入 map,对应模板中的 {{$fe: autoList}} 循环标记 map.put("autoList", listMap); // 基于模板和数据生成 Excel Workbook workbook = ExcelExportUtil.exportExcel(params, map); return workbook; } /** * 测试 Map 数据导出功能(使用 ExcelExportEntity) * * 测试内容: * - 手动构造 ExcelExportEntity 定义 Excel 列结构 * - 使用 Map 方式填充数据(适用于动态列场景) * - 设置 sheetName 和 ExcelType.XSSF 格式 * - 验证基于 ExportParams + ExcelExportEntity + Map 的导出方式 * * 数据结构: * - ExcelExportEntity:定义列名、字段名、列宽 * - Map数据:key 对应 ExcelExportEntity 的字段名 * * @return Workbook 填充数据后的工作簿对象 */ public static Workbook testMapDataExport() { // 定义 Excel 列结构 List entityList = new ArrayList<>(); entityList.add(new ExcelExportEntity("姓名", "name", 15)); entityList.add(new ExcelExportEntity("年龄", "age", 10)); entityList.add(new ExcelExportEntity("部门", "dept", 20)); entityList.add(new ExcelExportEntity("薪资", "salary", 15)); // 构造 Map 数据列表 List> result = new ArrayList<>(); for (int i = 0; i < 20; i++) { Map map = new HashMap<>(); map.put("name", "员工" + i); map.put("age", 25 + i); map.put("dept", i % 3 == 0 ? "研发部" : i % 3 == 1 ? "市场部" : "行政部"); map.put("salary", 8000 + i * 500); result.add(map); } // 设置导出参数 String sheetName = "员工信息表"; ExportParams exportParams = new ExportParams(null, sheetName); exportParams.setType(ExcelType.XSSF); // 导出 Excel Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, result); return workbook; } /** * 主方法:执行模板导出测试并保存文件 * * 执行步骤: * 1. 选择模板文件(test 或 testNextMarge) * 2. 调用 test() 方法基于模板生成 Workbook * 3. 检查保存目录是否存在 * 4. 将生成的 Workbook 保存到 D:/excel/testNew.xlsx * * 预期结果: * - 在 D:/excel/ 目录下生成 testNew.xlsx 文件 * - 文件内容基于模板结构,包含3行数据记录 * - 模板中的占位符被实际数据替换 * * @param args 命令行参数 * @throws IOException 文件操作异常 */ public static void main(String[] args) throws IOException { String temName = "test"; // 简单模板 String temNameNextM = "testNextMarge"; // 带合并单元格的复杂模板 // 测试1:使用复杂模板进行测试 Workbook workbook = test(temNameNextM); File savefile = new File(TEMPLATE_PATH); if (!savefile.exists()) { savefile.mkdirs(); } FileOutputStream fos = new FileOutputStream("D:/excel/testNew.xlsx"); workbook.write(fos); fos.close(); // 测试2:Map 数据导出测试 Workbook workbook2 = testMapDataExport(); FileOutputStream fos2 = new FileOutputStream("D:/excel/testMapDataExport.xlsx"); workbook2.write(fos2); fos2.close(); // 测试3:动态列导出示例 dynamicExport(); System.out.println("✅ 所有测试完成,文件已保存到 D:/excel/ 目录"); } /** * 动态列导出示例:@Excel(dynamic=true) */ public static void dynamicExport() throws IOException { List data = new ArrayList(); // 示例数据:每行的假期列表都包含 name/value 字段 DynamicRow row = new DynamicRow(); row.setUser("张三"); row.setHolidaysJson("[{\"name\":\"年假\",\"value\":\"10天\"},{\"name\":\"事假\",\"value\":\"31小时\"}]"); data.add(row); // 示例数据:每行的假期列表都包含 name/value 字段 DynamicRow row1 = new DynamicRow(); row1.setUser("李四"); row1.setHolidaysJson("[{\"name\":\"年假\",\"value\":\"20天\"},{\"name\":\"事假\",\"value\":\"41小时\"}]"); data.add(row1); ExportParams params = new ExportParams("动态列示例", "sheet1"); Workbook workbook = ExcelExportUtil.exportExcel(params, DynamicRow.class, data); File savefile = new File("D:\\excel"); if (!savefile.exists()) { savefile.mkdirs(); } FileOutputStream fos = new FileOutputStream( "D:\\excel\\dynamic.xlsx"); workbook.write(fos); fos.close(); } /** * 演示动态列的实体 */ public static class DynamicRow { @Excel(name = "姓名") private String user; @Excel(name = "假期json", dynamic = true, dynamicField = "name", dynamicVal = "value", dynamicKeepSelf = false) private String holidaysJson; public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getHolidaysJson() { return holidaysJson; } public void setHolidaysJson(String holidaysJson) { this.holidaysJson = holidaysJson; } } } ================================================ FILE: autopoi/src/test/java/DaoChuWordTest.java ================================================ import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.jeecgframework.poi.word.WordExportUtil; import java.io.File; import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.*; /** * @Description: Word 文档导出测试 * 测试功能: * 1. 测试基于 Word 模板的数据导出功能 * 2. 测试 Word 文档中的变量替换和列表数据填充 * 3. 验证 .docx 格式文档的模板导出效果 * * 测试场景: * - 使用预定义的 Word 模板(纳税信息.docx) * - 将 Map 数据填充到模板的占位符位置 * - 支持单个变量替换和列表数据循环生成 * * 应用场景: * - 生成标准化的合同文档 * - 批量生成报表文档 * - 根据数据自动生成通知、证明等文档 * * @author: scott * @date: 2020年09月16日 11:46 */ public class DaoChuWordTest { // 模板文件路径:项目根目录/autopoi/src/test/resources/templates/ private static final String TEMPLATE_PATH = System.getProperty("user.dir") + File.separator + "autopoi" + File.separator + "src" + File.separator + "test" + File.separator + "resources" + File.separator + "templates" + File.separator; /** * 主方法:执行 Word 文档导出测试 * * 测试内容: * - 准备纳税信息数据(单条记录和列表数据) * - 使用 Word 模板填充数据 * - 生成新的 Word 文档 * * 数据结构说明: * - taxlist: 纳税列表数据(可循环生成多行,当前为空) * - totalpreyear: 去年总额 * - totalthisyear: 今年总额 * - type: 税种类型 * - presum: 上期金额 * - thissum: 本期金额 * - curmonth: 当前月份 * - now: 当前时间 * * 模板语法: * - 单个变量:{{variableName}} * - 列表循环:{{$fe: listName t.field}} * * 预期结果: * - 在 D:/excel/ 目录下生成 纳税信息new.docx 文件 * - 文档中的占位符被实际数据替换 * - 如果有列表数据,会在文档中循环生成多行 * * @param args 命令行参数 * @throws Exception 文件操作异常 */ public static void main(String[] args) throws Exception { // 格式化当前时间 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String curTime = format.format(new Date()); // 准备数据 Map Map map = new HashMap(); // 准备列表数据(示例中被注释,可用于循环生成表格行) List> mapList = new ArrayList>(); // 示例:添加多条纳税记录到列表 // Map map1 = new HashMap(); // map1.put("type", "个人所得税"); // map1.put("presum", "1580"); // map1.put("thissum", "1750"); // map1.put("curmonth", "1-11月"); // map1.put("now", curTime); // mapList.add(map1); map.put("taxlist", mapList); // 列表数据(当前为空) map.put("totalpreyear", "2660"); // 去年总额 map.put("totalthisyear", "3400"); // 今年总额 // 单条记录数据 map.put("type", "增值税"); map.put("presum", "1080"); map.put("thissum", "1650"); map.put("curmonth", "1-11月"); map.put("now", curTime); // 基于模板导出 Word 文档(.docx 格式) XWPFDocument document = WordExportUtil.exportWord07(TEMPLATE_PATH + "纳税信息.docx", map); // 创建保存目录 File savefile = new File("D:\\excel"); if (!savefile.exists()) { savefile.mkdirs(); } // 保存生成的 Word 文档 FileOutputStream fos = new FileOutputStream("D:\\excel\\纳税信息new.docx"); document.write(fos); fos.close(); System.out.println("Word 文档导出完成!文件保存在:D:\\excel\\纳税信息new.docx"); } } ================================================ FILE: autopoi/src/test/java/ExcelToHtmlTest.java ================================================ import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelToHtmlUtil; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; /** * @Description: Excel 转 HTML 测试 * 测试功能: * 1. 测试将 Excel 文件转换为 HTML 表格格式 * 2. 验证 Excel 样式(字体、颜色、边框等)在 HTML 中的呈现 * 3. 测试复杂表格结构(合并单元格、多行表头)的转换效果 * * 应用场景: * - 在网页中预览 Excel 内容 * - 将 Excel 报表转换为 HTML 格式发送邮件 * - 在线展示 Excel 数据,无需下载文件 * * @author: autopoi */ public class ExcelToHtmlTest { // 模板文件路径:项目根目录/autopoi/src/test/resources/templates/ private static final String TEMPLATE_PATH = System.getProperty("user.dir") + File.separator + "autopoi" + File.separator + "src" + File.separator + "test" + File.separator + "resources" + File.separator + "templates" + File.separator; /** * 主方法:执行 Excel 转 HTML 测试 * * 测试内容: * - 读取 .xls 格式的 Excel 文件(专项支出用款申请书.xls) * - 将 Excel 内容转换为 HTML 表格格式 * - 保留原 Excel 的样式和布局 * - 将生成的 HTML 保存到文件 * * 转换特性: * - 自动处理合并单元格 * - 保留字体样式(大小、颜色、粗体等) * - 保留单元格边框和背景色 * - 保持表格布局和对齐方式 * * 预期结果: * - 在 D:/excel/ 目录下生成 专项支出用款申请书.html 文件 * - HTML 文件可在浏览器中打开查看 * - 表格样式与原 Excel 文件基本一致 * * @param args 命令行参数 * @throws Exception 文件读取或转换异常 */ public static void main(String[] args) throws Exception { // 读取 Excel 文件 File file = new File(TEMPLATE_PATH + "专项支出用款申请书.xls"); Workbook wb = new HSSFWorkbook(new FileInputStream(file)); // 将 Excel 转换为 HTML 表格 String html = ExcelToHtmlUtil.toTableHtml(wb); // 保存 HTML 文件 FileWriter fw = new FileWriter("D:/excel/专项支出用款申请书.html"); fw.write(html); fw.close(); System.out.println("Excel 转 HTML 完成!文件保存在:D:/excel/专项支出用款申请书.html"); } } ================================================ FILE: autopoi/src/test/java/ImportExcelTest.java ================================================ import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.jeecgframework.poi.excel.ExcelImportUtil; import org.jeecgframework.poi.excel.entity.ImportParams; import vo.TestDateEntity; import java.io.File; import java.util.List; /** * @Description: Excel 数据导入测试 * 测试功能: * 1. 测试从 Excel 文件中读取数据并转换为 Java 对象 * 2. 测试日期类型数据的导入和解析 * 3. 验证导入参数配置(标题行、表头行)的正确性 * * 测试场景: * - 读取 ExcelImportDateTest.xlsx 文件 * - 将 Excel 数据映射到 TestDateEntity 实体类 * - 支持自动类型转换和日期格式解析 * * @author: scott * @date: 2020年09月16日 11:46 */ public class ImportExcelTest { // 模板文件路径:项目根目录/autopoi/src/test/resources/templates/ private static final String TEMPLATE_PATH = System.getProperty("user.dir") + File.separator + "autopoi" + File.separator + "src" + File.separator + "test" + File.separator + "resources" + File.separator + "templates" + File.separator; /** * 主方法:执行 Excel 导入测试 * * 测试内容: * - 配置导入参数(标题行数、表头行数) * - 读取 Excel 文件并解析为实体对象列表 * - 验证导入的数据数量和内容正确性 * * 导入参数说明: * - TitleRows(1): 标题行占1行(通常是表格大标题) * - HeadRows(1): 表头行占1行(字段名称行) * - 实际数据从第3行开始读取 * * 预期结果: * - 成功读取 Excel 文件中的所有数据行 * - 数据正确映射到 TestDateEntity 对象 * - 日期字段正确解析为 Date 类型 * - 控制台输出数据总数和第2条数据的详细信息 * * @param args 命令行参数 * @throws Exception 文件读取或数据解析异常 */ public static void main(String[] args) throws Exception { // 配置导入参数 ImportParams params = new ImportParams(); params.setTitleRows(1); // 标题行数:1行 params.setHeadRows(1); // 表头行数:1行 // 指定要导入的 Excel 文件 File importFile = new File(TEMPLATE_PATH + "ExcelImportDateTest.xlsx"); // 执行导入:将 Excel 数据转换为 TestDateEntity 对象列表 List list = ExcelImportUtil.importExcel(importFile, TestDateEntity.class, params); // 输出导入结果 System.out.println("导入数据总数:" + list.size()); // 输出第2条数据的详细信息(索引为1) if (list.size() > 1) { System.out.println("第2条数据详情:"); System.out.println(ReflectionToStringBuilder.toString(list.get(1))); } } } ================================================ FILE: autopoi/src/test/java/Poi541ApiCompatibilityTest.java ================================================ import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.Assert; import org.junit.Test; import java.util.Date; /** * POI 5.4.1 API 兼容性测试 * 测试从 4.1.2 升级到 5.4.1 后的 API 变化 */ public class Poi541ApiCompatibilityTest { /** * 测试1: getCellType() 方法(替代 getCellTypeEnum()) */ @Test public void testGetCellType() { System.out.println("=== 测试 getCellType() 方法 ==="); Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("测试"); Row row = sheet.createRow(0); // 测试字符串类型 Cell cell1 = row.createCell(0); cell1.setCellValue("测试文本"); Assert.assertEquals("字符串类型判断", CellType.STRING, cell1.getCellType()); System.out.println(" ✅ STRING 类型: " + cell1.getCellType()); // 测试数字类型 Cell cell2 = row.createCell(1); cell2.setCellValue(123.45); Assert.assertEquals("数字类型判断", CellType.NUMERIC, cell2.getCellType()); System.out.println(" ✅ NUMERIC 类型: " + cell2.getCellType()); // 测试布尔类型 Cell cell3 = row.createCell(2); cell3.setCellValue(true); Assert.assertEquals("布尔类型判断", CellType.BOOLEAN, cell3.getCellType()); System.out.println(" ✅ BOOLEAN 类型: " + cell3.getCellType()); // 测试公式类型 Cell cell4 = row.createCell(3); cell4.setCellFormula("SUM(A1:B1)"); Assert.assertEquals("公式类型判断", CellType.FORMULA, cell4.getCellType()); System.out.println(" ✅ FORMULA 类型: " + cell4.getCellType()); System.out.println("✅ getCellType() 方法测试通过\n"); } /** * 测试2: DateUtil 类(替代 HSSFDateUtil) */ @Test public void testDateUtil() { System.out.println("=== 测试 DateUtil 类 ==="); Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("日期测试"); Row row = sheet.createRow(0); // 创建日期单元格 Cell cell = row.createCell(0); Date now = new Date(); cell.setCellValue(now); // 使用 DateUtil 判断是否为日期格式 CellStyle cellStyle = workbook.createCellStyle(); cellStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat("yyyy-MM-dd")); cell.setCellStyle(cellStyle); // 验证 DateUtil.isCellDateFormatted 方法 boolean isDate = DateUtil.isCellDateFormatted(cell); System.out.println(" ✅ DateUtil.isCellDateFormatted() 工作正常: " + isDate); // 测试数字转日期 double dateValue = cell.getNumericCellValue(); Date convertedDate = DateUtil.getJavaDate(dateValue); Assert.assertNotNull("日期转换不应为空", convertedDate); System.out.println(" ✅ DateUtil.getJavaDate() 转换成功: " + convertedDate); System.out.println("✅ DateUtil 类测试通过\n"); } /** * 测试3: getAlignment() 方法(替代 getAlignmentEnum()) */ @Test public void testGetAlignment() { System.out.println("=== 测试 getAlignment() 方法 ==="); Workbook workbook = new XSSFWorkbook(); CellStyle style = workbook.createCellStyle(); // 设置不同的对齐方式 style.setAlignment(HorizontalAlignment.CENTER); Assert.assertEquals("水平居中对齐", HorizontalAlignment.CENTER, style.getAlignment()); System.out.println(" ✅ 水平对齐: " + style.getAlignment()); style.setVerticalAlignment(VerticalAlignment.CENTER); Assert.assertEquals("垂直居中对齐", VerticalAlignment.CENTER, style.getVerticalAlignment()); System.out.println(" ✅ 垂直对齐: " + style.getVerticalAlignment()); System.out.println("✅ getAlignment() 方法测试通过\n"); } /** * 测试4: Font 索引类型变化(short -> int) */ @Test public void testFontIndexType() { System.out.println("=== 测试 Font 索引类型变化 ==="); Workbook workbook = new XSSFWorkbook(); // POI 5.x 中 getNumberOfFonts() 返回 int 类型 int fontCount = workbook.getNumberOfFonts(); System.out.println(" ✅ 字体数量 (int 类型): " + fontCount); // 遍历所有字体(使用 int 而不是 short) for (int i = 0; i < fontCount && i < 3; i++) { Font font = workbook.getFontAt(i); System.out.println(" ✅ 字体 " + i + ": " + font.getFontName()); } System.out.println("✅ Font 索引类型测试通过\n"); } /** * 测试5: Cell 值读取兼容性 */ @Test public void testCellValueCompatibility() { System.out.println("=== 测试 Cell 值读取兼容性 ==="); Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("值读取测试"); Row row = sheet.createRow(0); // 字符串值 Cell cell1 = row.createCell(0); cell1.setCellValue("测试"); if (cell1.getCellType() == CellType.STRING) { String value = cell1.getStringCellValue(); Assert.assertEquals("字符串值读取", "测试", value); System.out.println(" ✅ 字符串值: " + value); } // 数字值 Cell cell2 = row.createCell(1); cell2.setCellValue(100.5); if (cell2.getCellType() == CellType.NUMERIC) { double value = cell2.getNumericCellValue(); Assert.assertEquals("数字值读取", 100.5, value, 0.001); System.out.println(" ✅ 数字值: " + value); } // 布尔值 Cell cell3 = row.createCell(2); cell3.setCellValue(false); if (cell3.getCellType() == CellType.BOOLEAN) { boolean value = cell3.getBooleanCellValue(); Assert.assertFalse("布尔值读取", value); System.out.println(" ✅ 布尔值: " + value); } System.out.println("✅ Cell 值读取兼容性测试通过\n"); } /** * 测试6: 单元格样式兼容性 */ @Test public void testCellStyleCompatibility() { System.out.println("=== 测试单元格样式兼容性 ==="); Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("样式测试"); // 创建样式 CellStyle style = workbook.createCellStyle(); Font font = workbook.createFont(); font.setBold(true); font.setFontHeightInPoints((short) 14); font.setColor(IndexedColors.RED.getIndex()); style.setFont(font); // 设置对齐 style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); // 应用样式 Row row = sheet.createRow(0); Cell cell = row.createCell(0); cell.setCellValue("样式测试"); cell.setCellStyle(style); // 验证样式 CellStyle appliedStyle = cell.getCellStyle(); Assert.assertEquals("水平对齐", HorizontalAlignment.CENTER, appliedStyle.getAlignment()); Assert.assertEquals("垂直对齐", VerticalAlignment.CENTER, appliedStyle.getVerticalAlignment()); System.out.println(" ✅ 样式创建和应用正常"); System.out.println(" ✅ 对齐方式: " + appliedStyle.getAlignment()); System.out.println("✅ 单元格样式兼容性测试通过\n"); } /** * 测试7: Workbook 创建和基本操作 */ @Test public void testWorkbookBasicOperations() { System.out.println("=== 测试 Workbook 基本操作 ==="); // 创建 Workbook Workbook workbook = new XSSFWorkbook(); Assert.assertNotNull("Workbook 创建", workbook); System.out.println(" ✅ Workbook 创建成功"); // 创建 Sheet Sheet sheet1 = workbook.createSheet("Sheet1"); Sheet sheet2 = workbook.createSheet("Sheet2"); Assert.assertEquals("Sheet 数量", 2, workbook.getNumberOfSheets()); System.out.println(" ✅ 创建了 " + workbook.getNumberOfSheets() + " 个 Sheet"); // 创建行和单元格 Row row = sheet1.createRow(0); Cell cell = row.createCell(0); cell.setCellValue("POI 5.4.1 测试"); Assert.assertEquals("单元格值", "POI 5.4.1 测试", cell.getStringCellValue()); System.out.println(" ✅ 单元格值设置成功: " + cell.getStringCellValue()); System.out.println("✅ Workbook 基本操作测试通过\n"); } /** * 运行所有 API 兼容性测试 */ @Test public void runAllApiTests() { System.out.println("\n=========================================="); System.out.println(" POI 5.4.1 API 兼容性全面测试"); System.out.println("==========================================\n"); long startTime = System.currentTimeMillis(); testGetCellType(); testDateUtil(); testGetAlignment(); testFontIndexType(); testCellValueCompatibility(); testCellStyleCompatibility(); testWorkbookBasicOperations(); long totalTime = System.currentTimeMillis() - startTime; System.out.println("=========================================="); System.out.println("✅ 所有 API 兼容性测试通过!"); System.out.println(" 总耗时: " + totalTime + "ms"); System.out.println(" POI 5.4.1 升级成功,API 完全兼容"); System.out.println("==========================================\n"); } } ================================================ FILE: autopoi/src/test/java/Poi541ComprehensiveTest.java ================================================ import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.entity.TemplateExportParams; import org.jeecgframework.poi.excel.entity.enmus.ExcelType; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.jeecgframework.poi.handler.inter.IExcelExportServer; import org.junit.Test; import vo.TestEntity; import java.io.File; import java.io.FileOutputStream; import java.util.*; /** * POI 5.4.1 综合功能测试 * 测试升级后的各种导出功能是否正常 */ public class Poi541ComprehensiveTest { private static final String OUTPUT_DIR = "D:/excel/poi541test/"; /** * 测试1:多表头导出(Map数据 + 手动封装ExcelExportEntity) */ @Test public void testMultiHeaderExport() throws Exception { System.out.println("=== 测试多表头导出 ==="); List> dataList = new ArrayList>(); Map map1 = new HashMap<>(); map1.put("name", "小明"); map1.put("age", 21); map1.put("degree", 36); map1.put("link_name", "小八"); map1.put("link_age", 33); dataList.add(map1); Map map2 = new HashMap<>(); map2.put("name", "小王"); map2.put("age", 24); map2.put("degree", 37); map2.put("link_name", "小六"); map2.put("link_age", 26); dataList.add(map2); List entityList = new ArrayList<>(); // 一般表头 entityList.add(new ExcelExportEntity("姓名", "name")); entityList.add(new ExcelExportEntity("年龄", "age")); entityList.add(new ExcelExportEntity("体温", "degree")); // 多表头方式1:需要先添加子列,再添加父列(Map数据专用) // 子列需要使用三个参数的构造器,第三个参数为 true entityList.add(new ExcelExportEntity("姓名", "link_name", true)); entityList.add(new ExcelExportEntity("年龄", "link_age", true)); // 父列也需要设置 colspan=true,并设置 SubColumnList ExcelExportEntity contactEntity = new ExcelExportEntity("紧急联系人", "linkman", true); List subKeys = new ArrayList<>(); subKeys.add("link_name"); subKeys.add("link_age"); contactEntity.setSubColumnList(subKeys); entityList.add(contactEntity); // 导出 - 使用 XSSF 格式对应 .xlsx 文件 Workbook wb = ExcelExportUtil.exportExcel(new ExportParams("测试多表头", "sheetName", ExcelType.XSSF), entityList, dataList); // 保存文件 - 使用 .xlsx 扩展名 saveWorkbook(wb, "test1_multiheader.xlsx"); System.out.println("✅ 多表头导出测试完成"); } /** * 测试2:多Sheet导出(实体类方式) */ @Test public void testMultiSheetExport() throws Exception { System.out.println("=== 测试多Sheet导出 ==="); // 多个map,对应了多个sheet List> listMap = new ArrayList<>(); for (int i = 0; i < 3; i++) { Map map = new HashMap<>(); // 表格title map.put("title", getExportParams("测试Sheet" + (i + 1))); // 表格对应实体 map.put("entity", TestEntity.class); // 准备数据(实体类方式) List ls = new ArrayList<>(); for (int j = 0; j < 10; j++) { TestEntity testEntity = new TestEntity(); testEntity.setName("张三" + j); testEntity.setAge(18 + j); ls.add(testEntity); } map.put("data", ls); listMap.add(map); } // 导出 Workbook wb = ExcelExportUtil.exportExcel(listMap, ExcelType.HSSF); // 保存文件 saveWorkbook(wb, "test2_multisheet.xls"); System.out.println("✅ 多Sheet导出测试完成"); } /** * 测试3:模板导出(简单模板) */ @Test public void testTemplateExport() throws Exception { System.out.println("=== 测试模板导出 ==="); try { // 创建简单的测试模板(如果模板文件存在) String templatePath = "src/test/resources/templates/test.xlsx"; File templateFile = new File(templatePath); if (!templateFile.exists()) { System.out.println("⚠️ 模板文件不存在,跳过模板导出测试: " + templatePath); return; } TemplateExportParams params = new TemplateExportParams(templatePath); Map map = new HashMap<>(); map.put("title", "员工个人信息"); map.put("name", "大熊"); map.put("age", 22); map.put("company", "北京机器猫科技有限公司"); map.put("date", "2020-07-13"); Workbook workbook = ExcelExportUtil.exportExcel(params, map); // 保存文件 saveWorkbook(workbook, "test3_template.xlsx"); System.out.println("✅ 模板导出测试完成"); } catch (Exception e) { System.out.println("⚠️ 模板导出测试失败: " + e.getMessage()); } } /** * 测试4:复杂模板导出(带循环) */ @Test public void testComplexTemplateExport() throws Exception { System.out.println("=== 测试复杂模板导出(带循环)==="); try { String templatePath = "src/test/resources/templates/testNextMarge.xlsx"; File templateFile = new File(templatePath); if (!templateFile.exists()) { System.out.println("⚠️ 模板文件不存在,跳过复杂模板导出测试: " + templatePath); return; } TemplateExportParams params = new TemplateExportParams(templatePath); Map map = new HashMap<>(); map.put("title", "员工信息"); List> listMap = new ArrayList<>(); for (int i = 0; i < 6; i++) { Map lm = new HashMap<>(); lm.put("name", "王" + i); lm.put("age", "2" + i); lm.put("sex", i % 2 == 0 ? "1" : "2"); lm.put("date", new Date()); lm.put("salary", 1000 + i); listMap.add(lm); } map.put("maplist", listMap); Workbook workbook = ExcelExportUtil.exportExcel(params, map); // 保存文件 saveWorkbook(workbook, "test4_complex_template.xlsx"); System.out.println("✅ 复杂模板导出测试完成"); } catch (Exception e) { System.out.println("⚠️ 复杂模板导出测试失败: " + e.getMessage()); } } /** * 测试5:大数据导出(测试POI 5.4.1性能) */ @Test public void testBigDataExport() throws Exception { System.out.println("=== 测试大数据导出 ==="); Date start = new Date(); // 设置表格标题 ExportParams params = new ExportParams("POI 5.4.1大数据测试", "性能测试"); /** * 导出10万条数据(分10次,每次1万条) * 测试POI 5.4.1的性能 */ Workbook workbook = ExcelExportUtil.exportBigExcel( params, TestEntity.class, new IExcelExportServer() { @Override public List selectListForExcelExport(Object obj, int page) { System.out.println(" 正在导出第 " + (page + 1) + " 批数据..."); // page每次加一,当等于obj的值时返回空,代码结束 if (((int) obj) == page) { return null; } // 每次返回1万条数据 List list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { TestEntity client = new TestEntity(); client.setName("测试用户" + (page * 10000 + i)); client.setAge(18 + (i % 50)); list.add(client); } return list; } }, 10 // 总共导出10批 ); long timeUsed = new Date().getTime() - start.getTime(); System.out.println(" 导出10万条数据耗时: " + timeUsed + "ms (" + (timeUsed / 1000.0) + "秒)"); // 保存文件 saveWorkbook(workbook, "test5_bigdata_100k.xlsx"); System.out.println("✅ 大数据导出测试完成"); } /** * 测试6:Cell类型API兼容性测试 */ @Test public void testCellTypeCompatibility() throws Exception { System.out.println("=== 测试Cell类型API兼容性 ==="); List dataList = new ArrayList<>(); for (int i = 0; i < 5; i++) { TestEntity entity = new TestEntity(); entity.setName("用户" + i); entity.setAge(20 + i); dataList.add(entity); } ExportParams params = new ExportParams("API兼容性测试", "Sheet1", ExcelType.XSSF); Workbook wb = ExcelExportUtil.exportExcel(params, TestEntity.class, dataList); // 保存文件 saveWorkbook(wb, "test6_api_compatibility.xlsx"); System.out.println("✅ Cell类型API兼容性测试完成"); } /** * 测试7:Map数据导出 */ @Test public void testMapDataExport() throws Exception { System.out.println("=== 测试Map数据导出 ==="); List entityList = new ArrayList<>(); entityList.add(new ExcelExportEntity("姓名", "name", 15)); entityList.add(new ExcelExportEntity("年龄", "age", 10)); entityList.add(new ExcelExportEntity("部门", "dept", 20)); entityList.add(new ExcelExportEntity("薪资", "salary", 15)); List> dataList = new ArrayList<>(); for (int i = 0; i < 20; i++) { Map map = new HashMap<>(); map.put("name", "员工" + i); map.put("age", 25 + i); map.put("dept", i % 3 == 0 ? "研发部" : i % 3 == 1 ? "市场部" : "行政部"); map.put("salary", 8000 + i * 500); dataList.add(map); } Workbook wb = ExcelExportUtil.exportExcel( new ExportParams("Map数据导出", "员工表", ExcelType.XSSF), entityList, dataList ); saveWorkbook(wb, "test7_map_data.xlsx"); System.out.println("✅ Map数据导出测试完成"); } /** * 运行所有测试 */ @Test public void runAllTests() throws Exception { System.out.println("\n=========================================="); System.out.println(" POI 5.4.1 升级后功能综合测试"); System.out.println("==========================================\n"); long startTime = System.currentTimeMillis(); try { testMultiHeaderExport(); System.out.println(); testMultiSheetExport(); System.out.println(); testTemplateExport(); System.out.println(); testComplexTemplateExport(); System.out.println(); testCellTypeCompatibility(); System.out.println(); testMapDataExport(); System.out.println(); // 大数据测试放在最后(比较耗时) testBigDataExport(); } catch (Exception e) { System.err.println("❌ 测试过程中出现异常: " + e.getMessage()); e.printStackTrace(); throw e; } long totalTime = System.currentTimeMillis() - startTime; System.out.println("\n=========================================="); System.out.println("✅ 所有测试完成!"); System.out.println(" 总耗时: " + totalTime + "ms (" + (totalTime / 1000.0) + "秒)"); System.out.println(" 输出目录: " + OUTPUT_DIR); System.out.println("==========================================\n"); } /** * 获取导出参数 */ private static ExportParams getExportParams(String name) { // 表格名称,sheet名称,导出版本 return new ExportParams(name, name, ExcelType.HSSF); } /** * 保存Workbook到文件 */ private void saveWorkbook(Workbook workbook, String fileName) throws Exception { File saveDir = new File(OUTPUT_DIR); if (!saveDir.exists()) { saveDir.mkdirs(); } String filePath = OUTPUT_DIR + fileName; FileOutputStream fos = null; try { fos = new FileOutputStream(filePath); workbook.write(fos); fos.flush(); } finally { // 先关闭输出流 if (fos != null) { try { fos.close(); } catch (Exception e) { e.printStackTrace(); } } // 再关闭 Workbook (POI 5.x 需要显式关闭) if (workbook != null) { try { workbook.close(); } catch (Exception e) { e.printStackTrace(); } } } System.out.println(" 文件已保存: " + filePath); } } ================================================ FILE: autopoi/src/test/java/TestImageScale.java ================================================ import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.consts.ImageScaleMode; import org.jeecgframework.poi.entity.ImageEntity; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.entity.TemplateExportParams; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 图片缩放功能测试类 * 测试模板导出中的图片缩放功能 * for [issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式 * * @author chenrui * @date 2025-10-28 */ public class TestImageScale { // 模板文件路径 - 使用相对路径,确保所有人都能使用 private static final String TEMPLATE_PATH = "autopoi/src/test/resources/templates/"; // 测试图片路径 - 使用相对路径,图片已放在 resources/templates 下 private static final String TEST_IMAGE_PATH = "autopoi/src/test/resources/templates/dakytot.jpeg"; /** * 获取模板导出参数配置 */ public static TemplateExportParams getTemplateParams(String name) { return new TemplateExportParams(TEMPLATE_PATH + name + ".xlsx"); } /** * 测试图片缩放功能 * 测试3种不同的缩放模式: * - imageLSTC: 拉伸填充 (scaleMode = ImageScaleMode.STRETCH) * - imageDBL: 等比例缩放适应 (scaleMode = ImageScaleMode.FIT) * - imageYT: 不缩放(原始大小) (scaleMode = ImageScaleMode.ORIGINAL) */ public static Workbook testImageScaling(String templateName) { try { System.out.println("开始测试图片缩放功能,模板: " + templateName); TemplateExportParams params = getTemplateParams(templateName); System.out.println("模板路径: " + params.getTemplateUrl()); Map map = new HashMap(); // 构造列表数据(3条记录) List> listMap = new ArrayList>(); for (int i = 0; i < 3; i++) { Map lm = new HashMap(); lm.put("name", "用户" + (i + 1)); lm.put("id", i + 1); // 1. 拉伸填充 ImageEntity imageLSTC = new ImageEntity(); imageLSTC.setHeight(200); imageLSTC.setWidth(300); imageLSTC.setUrl(TEST_IMAGE_PATH); imageLSTC.setScaleModeEnum(ImageScaleMode.STRETCH); // 拉伸填充 lm.put("imageLSTC", imageLSTC); // 2. 等比例缩放适应 ImageEntity imageDBL = new ImageEntity(); imageDBL.setHeight(200); imageDBL.setWidth(300); imageDBL.setUrl(TEST_IMAGE_PATH); imageDBL.setScaleModeEnum(ImageScaleMode.FIT); // 等比例缩放适应 lm.put("imageDBL", imageDBL); // 3. 不缩放(原始大小) ImageEntity imageYT = new ImageEntity(); imageYT.setHeight(200); imageYT.setWidth(300); imageYT.setUrl(TEST_IMAGE_PATH); imageYT.setScaleModeEnum(ImageScaleMode.ORIGINAL); // 不缩放(原始大小) lm.put("imageYT", imageYT); listMap.add(lm); } // 将列表数据放入 map,对应模板中的 {{$fe: autoList}} 循环标记 map.put("autoList", listMap); System.out.println("数据准备完成,开始导出Excel..."); // 基于模板和数据生成 Excel Workbook workbook = ExcelExportUtil.exportExcel(params, map); System.out.println("Excel导出完成,workbook: " + (workbook != null ? "成功" : "失败")); return workbook; } catch (Exception e) { System.err.println("测试图片缩放功能失败: " + e.getMessage()); e.printStackTrace(); return null; } } /** * 主方法:执行图片缩放测试 */ public static void main(String[] args) throws IOException { System.out.println("=== 图片缩放功能测试开始 ==="); String templateName = "testImageScale"; // 使用test模板 String outputDir = "/Users/chenrui/Downloads"; try { // 测试1:本地图片缩放 System.out.println("测试1:本地图片缩放功能"); Workbook workbook1 = testImageScaling(templateName); File saveDir = new File(outputDir); if (!saveDir.exists()) { saveDir.mkdirs(); } FileOutputStream fos1 = new FileOutputStream(outputDir + "/ImageScaleTest_Local.xlsx"); workbook1.write(fos1); fos1.close(); workbook1.close(); System.out.println("=== 图片缩放功能测试完成 ==="); System.out.println("测试说明:"); System.out.println("- imageLSTC: 拉伸填充 (scaleMode = ImageScaleMode.STRETCH)"); System.out.println("- imageDBL: 等比例缩放适应 (scaleMode = ImageScaleMode.FIT)"); System.out.println("- imageYT: 不缩放(原始大小) (scaleMode = ImageScaleMode.ORIGINAL)"); } catch (Exception e) { System.err.println("❌ 测试失败: " + e.getMessage()); e.printStackTrace(); } } } ================================================ FILE: autopoi/src/test/java/vo/TestDateEntity.java ================================================ package vo; import org.jeecgframework.poi.excel.annotation.Excel; import java.time.LocalDate; import java.time.LocalDateTime; /** * @Description: TODO * @author: lsq * @date: 2024年07月31日 10:31 */ public class TestDateEntity { @Excel(name = "localdate", format = "yyyy-MM-dd") private LocalDate localDate; @Excel(name = "localdatetime", format = "yyyy-MM-dd") private LocalDateTime localDateTime; public LocalDate getLocalDate() { return localDate; } public void setLocalDate(LocalDate localDate) { this.localDate = localDate; } public LocalDateTime getLocalDateTime() { return localDateTime; } public void setLocalDateTime(LocalDateTime localDateTime) { this.localDateTime = localDateTime; } } ================================================ FILE: autopoi/src/test/java/vo/TestEntity.java ================================================ package vo; import org.jeecgframework.poi.excel.annotation.Excel; /** * @Description: TODO * @author: lsq * @date: 2021年02月02日 16:31 */ public class TestEntity { @Excel(name = "姓名", width = 15) private String name; @Excel(name = "年龄", width = 15) private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } } ================================================ FILE: autopoi-spring-boot-2-starter/pom.xml ================================================ 4.0.0 org.jeecgframework autopoi-parent 2.0.4 autopoi-spring-boot-2-starter autopoi-spring-boot-2-starter 1.8 1.8 1.8 org.jeecgframework autopoi ${autopoi.version} commons-io commons-io org.springframework spring-webmvc true javax.servlet servlet-api 2.5 provided true org.apache.maven.plugins maven-compiler-plugin 3.11.0 1.8 1.8 8 UTF-8 ================================================ FILE: autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/def/BasePOIConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.def; /** * 基础POI常量 * * @author JEECG * @date 2014年6月30日 下午9:23:37 */ interface BasePOIConstants { /** * 注解对象 */ public final static String CLASS = "entity"; /** * 表格参数 */ public final static String PARAMS = "params"; /** * 下载文件名称 */ public final static String FILE_NAME = "fileName"; } ================================================ FILE: autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/def/MapExcelConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.def; /** * 正常导出Excel * * @Author JEECG on 14-3-8. 静态常量 */ public interface MapExcelConstants extends BasePOIConstants { /** * 单Sheet导出 */ public final static String JEECG_MAP_EXCEL_VIEW = "jeecgMapExcelView"; /** * Entity List */ public final static String ENTITY_LIST = "data"; /** * 数据列表 */ public final static String MAP_LIST = "mapList"; } ================================================ FILE: autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/def/NormalExcelConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.def; /** * 正常导出Excel * * @Author JEECG on 14-3-8. 静态常量 */ public interface NormalExcelConstants extends BasePOIConstants { /** * 单Sheet导出 */ public final static String JEECG_ENTITY_EXCEL_VIEW = "jeecgEntityExcelView"; /** * 数据列表 */ public final static String DATA_LIST = "data"; /** * 多Sheet 对象 */ public final static String MAP_LIST = "mapList"; /** * 导出字段自定义 */ public final static String EXPORT_FIELDS = "exportFields"; /** * 自定义导出服务 * for [issues/8652]excel导出大数据问题 #8652 */ public final static String EXPORT_SERVER = "excelExportServer"; /** * 查询参数 * for [issues/8652]excel导出大数据问题 #8652 */ public final static String QUERY_PARAMS = "queryParams"; } ================================================ FILE: autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/def/TemplateExcelConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.def; /** * 模板Excel导出常量 * * @author JEECG * @date 2014年6月30日 下午9:26:52 */ public interface TemplateExcelConstants extends BasePOIConstants { /** * 模板导出 */ public final static String JEECG_TEMPLATE_EXCEL_VIEW = "jeecgTemplateExcelView"; /** * 数据列表 */ public final static String LIST_DATA = "list"; /** * 模板参数 */ public final static String MAP_DATA = "map"; } ================================================ FILE: autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/def/TemplateWordConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.def; /** * Word 导出模板常量 * * @author JEECG * @date 2014年7月24日 下午11:26:46 */ public interface TemplateWordConstants extends BasePOIConstants { /** * 模板导出 */ public final static String JEECG_TEMPLATE_WORD_VIEW = "jeecgTemplateWordView"; /** * 数据列表 */ public final static String URL = "url"; /** * 模板参数 */ public final static String MAP_DATA = "map"; } ================================================ FILE: autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgEntityExcelView.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.view; import java.util.Collection; import java.util.List; import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.def.NormalExcelConstants; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.export.ExcelExportServer; import org.jeecgframework.poi.handler.inter.IExcelExportServer; import org.jeecgframework.poi.handler.inter.IExcelExportServerEnhanced; import org.springframework.stereotype.Controller; /** * Entity 实体数据对象导出 * @Author JEECG * */ @SuppressWarnings("unchecked") @Controller(NormalExcelConstants.JEECG_ENTITY_EXCEL_VIEW) public class JeecgEntityExcelView extends MiniAbstractExcelView { public JeecgEntityExcelView() { super(); } @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { String codedFileName = "临时文件"; Workbook workbook = null; //---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义-------- String[] exportFields = null; Object exportFieldStr = model.get(NormalExcelConstants.EXPORT_FIELDS); if(exportFieldStr!=null && exportFieldStr!=""){ exportFields = exportFieldStr.toString().split(","); } //---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义-------- if (model.containsKey(NormalExcelConstants.MAP_LIST)) { List> list = (List>) model.get(NormalExcelConstants.MAP_LIST); if (list.size() == 0) { throw new RuntimeException("MAP_LIST IS NULL"); } workbook = ExcelExportUtil.exportExcel((ExportParams) list.get(0).get(NormalExcelConstants.PARAMS), (Class) list.get(0).get(NormalExcelConstants.CLASS), (Collection) list.get(0).get(NormalExcelConstants.DATA_LIST),exportFields); for (int i = 1; i < list.size(); i++) { new ExcelExportServer().createSheet(workbook, (ExportParams) list.get(i).get(NormalExcelConstants.PARAMS), (Class) list.get(i).get(NormalExcelConstants.CLASS), (Collection) list.get(i).get(NormalExcelConstants.DATA_LIST),exportFields); } } else if(model.containsKey(NormalExcelConstants.EXPORT_SERVER)){ //update-begin---author:chenrui ---date:20251104 for:[QQYUN-13964]演示系统数据量大,点击没反应------------ Object exportServerObj = model.get(NormalExcelConstants.EXPORT_SERVER); if(exportServerObj instanceof IExcelExportServer){ //update-begin---author:chenrui ---date:20250812 for:[issues/8652]excel导出大数据问题 #8652------------ workbook = ExcelExportUtil.exportBigExcel((ExportParams) model.get(NormalExcelConstants.PARAMS), (Class) model.get(NormalExcelConstants.CLASS), (IExcelExportServer) model.get(NormalExcelConstants.EXPORT_SERVER), model.get(NormalExcelConstants.QUERY_PARAMS)); //update-end---author:chenrui ---date:20250812 for:[issues/8652]excel导出大数据问题 #8652------------ } else if(exportServerObj instanceof IExcelExportServerEnhanced){ workbook = ExcelExportUtil.exportBigExcelEnhanced((ExportParams) model.get(NormalExcelConstants.PARAMS), (Class) model.get(NormalExcelConstants.CLASS), (IExcelExportServerEnhanced) exportServerObj, model.get(NormalExcelConstants.QUERY_PARAMS)); } //update-end---author:chenrui ---date:20251104 for:[QQYUN-13964]演示系统数据量大,点击没反应------------ } else { workbook = ExcelExportUtil.exportExcel((ExportParams) model.get(NormalExcelConstants.PARAMS), (Class) model.get(NormalExcelConstants.CLASS), (Collection) model.get(NormalExcelConstants.DATA_LIST),exportFields); } if (model.containsKey(NormalExcelConstants.FILE_NAME)) { codedFileName = (String) model.get(NormalExcelConstants.FILE_NAME); } if (workbook instanceof HSSFWorkbook) { codedFileName += HSSF; } else { codedFileName += XSSF; } if (isIE(request)) { codedFileName = java.net.URLEncoder.encode(codedFileName, "UTF8"); } else { codedFileName = new String(codedFileName.getBytes("UTF-8"), "ISO-8859-1"); } response.setHeader("content-disposition", "attachment;filename=" + codedFileName); ServletOutputStream out = response.getOutputStream(); workbook.write(out); out.flush(); } } ================================================ FILE: autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgMapExcelView.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.view; import java.util.Collection; import java.util.List; import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.def.MapExcelConstants; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.springframework.stereotype.Controller; /** * Map 数据对象接口导出 * * @author JEECG * @date 2014年11月25日 下午3:26:32 */ @SuppressWarnings("unchecked") @Controller(MapExcelConstants.JEECG_MAP_EXCEL_VIEW) public class JeecgMapExcelView extends MiniAbstractExcelView { public JeecgMapExcelView() { super(); } @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { String codedFileName = "临时文件"; Workbook workbook = ExcelExportUtil.exportExcel((ExportParams) model.get(MapExcelConstants.PARAMS), (List) model.get(MapExcelConstants.ENTITY_LIST), (Collection>) model.get(MapExcelConstants.MAP_LIST)); if (model.containsKey(MapExcelConstants.FILE_NAME)) { codedFileName = (String) model.get(MapExcelConstants.FILE_NAME); } if (workbook instanceof HSSFWorkbook) { codedFileName += HSSF; } else { codedFileName += XSSF; } if (isIE(request)) { codedFileName = java.net.URLEncoder.encode(codedFileName, "UTF8"); } else { codedFileName = new String(codedFileName.getBytes("UTF-8"), "ISO-8859-1"); } response.setHeader("content-disposition", "attachment;filename=" + codedFileName); ServletOutputStream out = response.getOutputStream(); workbook.write(out); out.flush(); } } ================================================ FILE: autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgTemplateExcelView.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.view; import java.util.List; import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.def.NormalExcelConstants; import org.jeecgframework.poi.excel.def.TemplateExcelConstants; import org.jeecgframework.poi.excel.entity.TemplateExportParams; import org.springframework.stereotype.Controller; /** * Excel 模板导出 * * @author JEECG * @date 2014年6月30日 下午9:15:49 */ @SuppressWarnings("unchecked") @Controller(TemplateExcelConstants.JEECG_TEMPLATE_EXCEL_VIEW) public class JeecgTemplateExcelView extends MiniAbstractExcelView { public JeecgTemplateExcelView() { super(); } @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { String codedFileName = "临时文件"; Workbook workbook = ExcelExportUtil.exportExcel((TemplateExportParams) model.get(TemplateExcelConstants.PARAMS), (Class) model.get(TemplateExcelConstants.CLASS), (List) model.get(TemplateExcelConstants.LIST_DATA), (Map) model.get(TemplateExcelConstants.MAP_DATA)); if (model.containsKey(NormalExcelConstants.FILE_NAME)) { codedFileName = (String) model.get(NormalExcelConstants.FILE_NAME); } if (workbook instanceof HSSFWorkbook) { codedFileName += HSSF; } else { codedFileName += XSSF; } if (isIE(request)) { codedFileName = java.net.URLEncoder.encode(codedFileName, "UTF8"); } else { codedFileName = new String(codedFileName.getBytes("UTF-8"), "ISO-8859-1"); } response.setHeader("content-disposition", "attachment;filename=" + codedFileName); ServletOutputStream out = response.getOutputStream(); workbook.write(out); out.flush(); } } ================================================ FILE: autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgTemplateWordView.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.view; import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.jeecgframework.poi.excel.def.TemplateWordConstants; import org.jeecgframework.poi.word.WordExportUtil; import org.springframework.stereotype.Controller; import org.springframework.web.servlet.view.AbstractView; /** * Word模板导出 * * @author JEECG * @date 2014年6月30日 下午9:15:49 */ @SuppressWarnings("unchecked") @Controller(TemplateWordConstants.JEECG_TEMPLATE_WORD_VIEW) public class JeecgTemplateWordView extends AbstractView { private static final String CONTENT_TYPE = "application/msword"; public JeecgTemplateWordView() { setContentType(CONTENT_TYPE); } public boolean isIE(HttpServletRequest request) { return (request.getHeader("USER-AGENT").toLowerCase().indexOf("msie") > 0 || request.getHeader("USER-AGENT").toLowerCase().indexOf("rv:11.0") > 0) ? true : false; } @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { String codedFileName = "临时文件.docx"; if (model.containsKey(TemplateWordConstants.FILE_NAME)) { codedFileName = (String) model.get(TemplateWordConstants.FILE_NAME) + ".docx"; } if (isIE(request)) { codedFileName = java.net.URLEncoder.encode(codedFileName, "UTF8"); } else { codedFileName = new String(codedFileName.getBytes("UTF-8"), "ISO-8859-1"); } response.setHeader("content-disposition", "attachment;filename=" + codedFileName); XWPFDocument document = WordExportUtil.exportWord07((String) model.get(TemplateWordConstants.URL), (Map) model.get(TemplateWordConstants.MAP_DATA)); ServletOutputStream out = response.getOutputStream(); document.write(out); out.flush(); } } ================================================ FILE: autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/MiniAbstractExcelView.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.view; import javax.servlet.http.HttpServletRequest; import org.springframework.web.servlet.view.AbstractView; /** * 基础抽象Excel View * * @author JEECG * @date 2015年2月28日 下午1:41:05 */ public abstract class MiniAbstractExcelView extends AbstractView { private static final String CONTENT_TYPE = "application/vnd.ms-excel"; protected static final String HSSF = ".xls"; protected static final String XSSF = ".xlsx"; public MiniAbstractExcelView() { setContentType(CONTENT_TYPE); } protected boolean isIE(HttpServletRequest request) { return (request.getHeader("USER-AGENT").toLowerCase().indexOf("msie") > 0 || request.getHeader("USER-AGENT").toLowerCase().indexOf("rv:11.0") > 0) ? true : false; } } ================================================ FILE: autopoi-spring-boot-3-starter/pom.xml ================================================ 4.0.0 org.jeecgframework autopoi-parent 2.0.4 autopoi-spring-boot-3-starter autopoi-spring-boot-3-starter 17 17 17 17 org.jeecgframework autopoi ${autopoi.version} commons-io commons-io org.springframework spring-webmvc 6.2.1 true jakarta.servlet jakarta.servlet-api 6.1.0 provided true org.apache.maven.plugins maven-compiler-plugin 3.11.0 17 17 17 UTF-8 ================================================ FILE: autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/def/BasePOIConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.def; /** * 基础POI常量 * * @author JEECG * @date 2014年6月30日 下午9:23:37 */ interface BasePOIConstants { /** * 注解对象 */ public final static String CLASS = "entity"; /** * 表格参数 */ public final static String PARAMS = "params"; /** * 下载文件名称 */ public final static String FILE_NAME = "fileName"; } ================================================ FILE: autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/def/MapExcelConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.def; /** * 正常导出Excel * * @Author JEECG on 14-3-8. 静态常量 */ public interface MapExcelConstants extends BasePOIConstants { /** * 单Sheet导出 */ public final static String JEECG_MAP_EXCEL_VIEW = "jeecgMapExcelView"; /** * Entity List */ public final static String ENTITY_LIST = "data"; /** * 数据列表 */ public final static String MAP_LIST = "mapList"; } ================================================ FILE: autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/def/NormalExcelConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.def; /** * 正常导出Excel * * @Author JEECG on 14-3-8. 静态常量 */ public interface NormalExcelConstants extends BasePOIConstants { /** * 单Sheet导出 */ public final static String JEECG_ENTITY_EXCEL_VIEW = "jeecgEntityExcelView"; /** * 数据列表 */ public final static String DATA_LIST = "data"; /** * 多Sheet 对象 */ public final static String MAP_LIST = "mapList"; /** * 导出字段自定义 */ public final static String EXPORT_FIELDS = "exportFields"; /** * 自定义导出服务 * for [issues/8652]excel导出大数据问题 #8652 */ public final static String EXPORT_SERVER = "excelExportServer"; /** * 查询参数 * for [issues/8652]excel导出大数据问题 #8652 */ public final static String QUERY_PARAMS = "queryParams"; } ================================================ FILE: autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/def/TemplateExcelConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.def; /** * 模板Excel导出常量 * * @author JEECG * @date 2014年6月30日 下午9:26:52 */ public interface TemplateExcelConstants extends BasePOIConstants { /** * 模板导出 */ public final static String JEECG_TEMPLATE_EXCEL_VIEW = "jeecgTemplateExcelView"; /** * 数据列表 */ public final static String LIST_DATA = "list"; /** * 模板参数 */ public final static String MAP_DATA = "map"; } ================================================ FILE: autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/def/TemplateWordConstants.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.def; /** * Word 导出模板常量 * * @author JEECG * @date 2014年7月24日 下午11:26:46 */ public interface TemplateWordConstants extends BasePOIConstants { /** * 模板导出 */ public final static String JEECG_TEMPLATE_WORD_VIEW = "jeecgTemplateWordView"; /** * 数据列表 */ public final static String URL = "url"; /** * 模板参数 */ public final static String MAP_DATA = "map"; } ================================================ FILE: autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgEntityExcelView.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.view; import java.util.Collection; import java.util.List; import java.util.Map; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.def.NormalExcelConstants; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.export.ExcelExportServer; import org.jeecgframework.poi.handler.inter.IExcelExportServer; import org.jeecgframework.poi.handler.inter.IExcelExportServerEnhanced; import org.springframework.stereotype.Controller; /** * Entity 实体数据对象导出 * @Author JEECG * */ @SuppressWarnings("unchecked") @Controller(NormalExcelConstants.JEECG_ENTITY_EXCEL_VIEW) public class JeecgEntityExcelView extends MiniAbstractExcelView { public JeecgEntityExcelView() { super(); } @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { String codedFileName = "临时文件"; Workbook workbook = null; //---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义-------- String[] exportFields = null; Object exportFieldStr = model.get(NormalExcelConstants.EXPORT_FIELDS); if(exportFieldStr!=null && exportFieldStr!=""){ exportFields = exportFieldStr.toString().split(","); } //---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义-------- if (model.containsKey(NormalExcelConstants.MAP_LIST)) { List> list = (List>) model.get(NormalExcelConstants.MAP_LIST); if (list.size() == 0) { throw new RuntimeException("MAP_LIST IS NULL"); } workbook = ExcelExportUtil.exportExcel((ExportParams) list.get(0).get(NormalExcelConstants.PARAMS), (Class) list.get(0).get(NormalExcelConstants.CLASS), (Collection) list.get(0).get(NormalExcelConstants.DATA_LIST),exportFields); for (int i = 1; i < list.size(); i++) { new ExcelExportServer().createSheet(workbook, (ExportParams) list.get(i).get(NormalExcelConstants.PARAMS), (Class) list.get(i).get(NormalExcelConstants.CLASS), (Collection) list.get(i).get(NormalExcelConstants.DATA_LIST),exportFields); } } else if(model.containsKey(NormalExcelConstants.EXPORT_SERVER)){ //update-begin---author:chenrui ---date:20251104 for:[QQYUN-13964]演示系统数据量大,点击没反应------------ Object exportServerObj = model.get(NormalExcelConstants.EXPORT_SERVER); if(exportServerObj instanceof IExcelExportServer){ //update-begin---author:chenrui ---date:20250812 for:[issues/8652]excel导出大数据问题 #8652------------ workbook = ExcelExportUtil.exportBigExcel((ExportParams) model.get(NormalExcelConstants.PARAMS), (Class) model.get(NormalExcelConstants.CLASS), (IExcelExportServer) model.get(NormalExcelConstants.EXPORT_SERVER), model.get(NormalExcelConstants.QUERY_PARAMS)); //update-end---author:chenrui ---date:20250812 for:[issues/8652]excel导出大数据问题 #8652------------ } else if(exportServerObj instanceof IExcelExportServerEnhanced){ workbook = ExcelExportUtil.exportBigExcelEnhanced((ExportParams) model.get(NormalExcelConstants.PARAMS), (Class) model.get(NormalExcelConstants.CLASS), (IExcelExportServerEnhanced) exportServerObj, model.get(NormalExcelConstants.QUERY_PARAMS)); } //update-end---author:chenrui ---date:20251104 for:[QQYUN-13964]演示系统数据量大,点击没反应------------ } else { workbook = ExcelExportUtil.exportExcel((ExportParams) model.get(NormalExcelConstants.PARAMS), (Class) model.get(NormalExcelConstants.CLASS), (Collection) model.get(NormalExcelConstants.DATA_LIST),exportFields); } if (model.containsKey(NormalExcelConstants.FILE_NAME)) { codedFileName = (String) model.get(NormalExcelConstants.FILE_NAME); } if (workbook instanceof HSSFWorkbook) { codedFileName += HSSF; } else { codedFileName += XSSF; } if (isIE(request)) { codedFileName = java.net.URLEncoder.encode(codedFileName, "UTF8"); } else { codedFileName = new String(codedFileName.getBytes("UTF-8"), "ISO-8859-1"); } response.setHeader("content-disposition", "attachment;filename=" + codedFileName); ServletOutputStream out = response.getOutputStream(); workbook.write(out); out.flush(); } } ================================================ FILE: autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgMapExcelView.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.view; import java.util.Collection; import java.util.List; import java.util.Map; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.def.MapExcelConstants; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity; import org.springframework.stereotype.Controller; /** * Map 数据对象接口导出 * * @author JEECG * @date 2014年11月25日 下午3:26:32 */ @SuppressWarnings("unchecked") @Controller(MapExcelConstants.JEECG_MAP_EXCEL_VIEW) public class JeecgMapExcelView extends MiniAbstractExcelView { public JeecgMapExcelView() { super(); } @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { String codedFileName = "临时文件"; Workbook workbook = ExcelExportUtil.exportExcel((ExportParams) model.get(MapExcelConstants.PARAMS), (List) model.get(MapExcelConstants.ENTITY_LIST), (Collection>) model.get(MapExcelConstants.MAP_LIST)); if (model.containsKey(MapExcelConstants.FILE_NAME)) { codedFileName = (String) model.get(MapExcelConstants.FILE_NAME); } if (workbook instanceof HSSFWorkbook) { codedFileName += HSSF; } else { codedFileName += XSSF; } if (isIE(request)) { codedFileName = java.net.URLEncoder.encode(codedFileName, "UTF8"); } else { codedFileName = new String(codedFileName.getBytes("UTF-8"), "ISO-8859-1"); } response.setHeader("content-disposition", "attachment;filename=" + codedFileName); ServletOutputStream out = response.getOutputStream(); workbook.write(out); out.flush(); } } ================================================ FILE: autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgTemplateExcelView.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.view; import java.util.List; import java.util.Map; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; import org.jeecgframework.poi.excel.ExcelExportUtil; import org.jeecgframework.poi.excel.def.NormalExcelConstants; import org.jeecgframework.poi.excel.def.TemplateExcelConstants; import org.jeecgframework.poi.excel.entity.TemplateExportParams; import org.springframework.stereotype.Controller; /** * Excel 模板导出 * * @author JEECG * @date 2014年6月30日 下午9:15:49 */ @SuppressWarnings("unchecked") @Controller(TemplateExcelConstants.JEECG_TEMPLATE_EXCEL_VIEW) public class JeecgTemplateExcelView extends MiniAbstractExcelView { public JeecgTemplateExcelView() { super(); } @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { String codedFileName = "临时文件"; Workbook workbook = ExcelExportUtil.exportExcel((TemplateExportParams) model.get(TemplateExcelConstants.PARAMS), (Class) model.get(TemplateExcelConstants.CLASS), (List) model.get(TemplateExcelConstants.LIST_DATA), (Map) model.get(TemplateExcelConstants.MAP_DATA)); if (model.containsKey(NormalExcelConstants.FILE_NAME)) { codedFileName = (String) model.get(NormalExcelConstants.FILE_NAME); } if (workbook instanceof HSSFWorkbook) { codedFileName += HSSF; } else { codedFileName += XSSF; } if (isIE(request)) { codedFileName = java.net.URLEncoder.encode(codedFileName, "UTF8"); } else { codedFileName = new String(codedFileName.getBytes("UTF-8"), "ISO-8859-1"); } response.setHeader("content-disposition", "attachment;filename=" + codedFileName); ServletOutputStream out = response.getOutputStream(); workbook.write(out); out.flush(); } } ================================================ FILE: autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgTemplateWordView.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.view; import java.util.Map; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.jeecgframework.poi.excel.def.TemplateWordConstants; import org.jeecgframework.poi.word.WordExportUtil; import org.springframework.stereotype.Controller; import org.springframework.web.servlet.view.AbstractView; /** * Word模板导出 * * @author JEECG * @date 2014年6月30日 下午9:15:49 */ @SuppressWarnings("unchecked") @Controller(TemplateWordConstants.JEECG_TEMPLATE_WORD_VIEW) public class JeecgTemplateWordView extends AbstractView { private static final String CONTENT_TYPE = "application/msword"; public JeecgTemplateWordView() { setContentType(CONTENT_TYPE); } public boolean isIE(HttpServletRequest request) { return (request.getHeader("USER-AGENT").toLowerCase().indexOf("msie") > 0 || request.getHeader("USER-AGENT").toLowerCase().indexOf("rv:11.0") > 0) ? true : false; } @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { String codedFileName = "临时文件.docx"; if (model.containsKey(TemplateWordConstants.FILE_NAME)) { codedFileName = (String) model.get(TemplateWordConstants.FILE_NAME) + ".docx"; } if (isIE(request)) { codedFileName = java.net.URLEncoder.encode(codedFileName, "UTF8"); } else { codedFileName = new String(codedFileName.getBytes("UTF-8"), "ISO-8859-1"); } response.setHeader("content-disposition", "attachment;filename=" + codedFileName); XWPFDocument document = WordExportUtil.exportWord07((String) model.get(TemplateWordConstants.URL), (Map) model.get(TemplateWordConstants.MAP_DATA)); ServletOutputStream out = response.getOutputStream(); document.write(out); out.flush(); } } ================================================ FILE: autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/MiniAbstractExcelView.java ================================================ /** * Copyright 2013-2015 JEECG (jeecgos@163.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 org.jeecgframework.poi.excel.view; import jakarta.servlet.http.HttpServletRequest; import org.springframework.web.servlet.view.AbstractView; /** * 基础抽象Excel View * * @author JEECG * @date 2015年2月28日 下午1:41:05 */ public abstract class MiniAbstractExcelView extends AbstractView { private static final String CONTENT_TYPE = "application/vnd.ms-excel"; protected static final String HSSF = ".xls"; protected static final String XSSF = ".xlsx"; public MiniAbstractExcelView() { setContentType(CONTENT_TYPE); } protected boolean isIE(HttpServletRequest request) { return (request.getHeader("USER-AGENT").toLowerCase().indexOf("msie") > 0 || request.getHeader("USER-AGENT").toLowerCase().indexOf("rv:11.0") > 0) ? true : false; } } ================================================ FILE: deploy.bat ================================================ cmd /k mvn clean deploy -P release -Dmaven.test.skip=true ================================================ FILE: docs/修改日志.log ================================================ -- author: liusq---date:20230407---for: [issue/4342]autopoi导出带副标题的数据表,副标题缺左边框 autopoi\src\main\java\org\jeecgframework\poi\excel\export\ExcelExportServer.java -- author: liusq---date:20230407---for: [issue/4342]autopoi导出带副标题的数据表,副标题缺左边框 -- author: liusq---date:20230410---for: [issue/4415]autopoi-web 导入图片字段时无法指定保存路径 autopoi\src\main\java\org\jeecgframework\poi\excel\imports\ExcelImportServer.java autopoi\src\main\java\org\jeecgframework\poi\excel\imports\base\ImportFileServiceI.java autopoi\src\main\java\org\jeecgframework\poi\excel\annotation\Excel.java -- author: liusq---date:20230410---for: [issue/4415]autopoi-web 导入图片字段时无法指定保存路径 ---author:scott ---date:2023-12-05 for:【jeecg-boot/issues/5538】导出表格设置了数字格式导出之后仍然是文本格式,并且无法进行计算 #5538- org\jeecgframework\poi\excel\export\base\ExcelExportBase.java ---author:scott ---date::2023-12-05 for:【jeecg-boot/issues/5538】导出表格设置了数字格式导出之后仍然是文本格式,并且无法进行计算 #5538-- ---author:scott ---date::2023-12-05 for:【jeecg-boot/issues/5528】POI导出一对多只有一条数据时,疯狂打印错误日志 #5528 org\jeecgframework\poi\excel\export\base\ExcelExportBase.java ---author:scott ---date::2023-12-05 for:【jeecg-boot/issues/5528】POI导出一对多只有一条数据时,疯狂打印错误日志 #5528 ---author:liusq ---date::2023-12-07 for:[issues/5538]导出表格设置了数字格式导出之后仍然是文本格式,并且无法进行计算 autopoi-framework\autopoi\src\main\java\org\jeecgframework\poi\excel\export\base\ExcelExportBase.java ---author:liusq ---date::2023-12-07 for:[issues/5538]导出表格设置了数字格式导出之后仍然是文本格式,并且无法进行计算 ---author:scott ---date:2023-12-08 for:代码写法兼容改造--- org\jeecgframework\poi\excel\annotation\Excel.java org\jeecgframework\poi\excel\imports\ExcelImportServer.java ---author:scott ---date:2023-12-08 for:代码写法兼容改造--- ---author:chenrui---date:2024/1/2-----for:[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题--- autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/MergedRegionHelper.java autopoi-web/src/test/java/DaoChuTest.java autopoi-web/src/test/resources/templates/testNextMarge.xlsx ---author:chenrui---date:2024/1/2-----for:[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题--- ---author:chenrui---date:2024/1/3-----for:[issue/#5248]加强继承扩展便利性--- autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/Excel.java autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelExportUtil.java ---author:chenrui---date:2024/1/3-----for:[issue/#5248]加强继承扩展便利性--- ---author:chenrui---date:2024/3/8-----for:[QQYUN-8394]Excel导入时空行校验问题--- autopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java ---author:chenrui---date:2024/3/8-----for:[QQYUN-8394]Excel导入时空行校验问题--- ---author:chenrui---date:2024/4/3-----for:[issue/#5933]增加清除缓存方法--- autopoi/src/main/java/org/jeecgframework/poi/cache/manager/POICacheManager.java ---author:chenrui---date:2024/4/3-----for:[issue/#5933]增加清除缓存方法--- ---author:chenrui---date:2024/4/3-----for:生成代码后子表图片无法导出(流)--- autopoi/src/main/java/org/jeecgframework/poi/excel/export/base/ExportBase.java ---author:chenrui---date:2024/4/3-----for:生成代码后子表图片无法导出(流)--- ---author:chenrui---date:2024/4/3-----for:[issue/#6025/#6040]子表图片导入报错--- autopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java ---author:chenrui---date:2024/4/3-----for:[issue/#6025/#6040]子表图片导入报错--- ---author:chenrui---date:2024/4/3-----for:[issue/#5987]嵌入单元格图片无法导入--- autopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java autopoi/src/main/java/org/jeecgframework/poi/util/PoiPublicUtil.java autopoi/pom.xml pom.xml ---author:chenrui---date:2024/4/3-----for:[issue/#5987]嵌入单元格图片无法导入--- ---author:chenrui---date:2024/4/7-----for:[QQYUN-8898]不依赖hutool,xml解析改为dom--- pom.xml autopoi/pom.xml autopoi/src/main/java/org/jeecgframework/poi/util/PoiPublicUtil.java ---author:chenrui---date:2024/4/7-----for:[QQYUN-8898]不依赖hutool,xml解析改为dom--- ---author:chenrui---date:2024/4/24-----for:[QQYUN-9048]负数被识别成非数字--- src/main/java/org/jeecgframework/poi/util/ExcelUtil.java ---author:chenrui---date:2024/4/24-----for:[QQYUN-9048]负数被识别成非数字--- ---author:liusq---date:2024/7/30-----for:TV360X-1953 execl转html报错d != org.apache.poi.ss.usermodel.FillPatternType--- src\main\java\org\jeecgframework\poi\excel\html\helper\StylerHelper.java src\test\resources\templates\专项支出用款申请书.xls src\test\java\ExcelToHtmlTest.java ---author:liusq---date:2024/7/30-----for:TV360X-1953 execl转html报错d != org.apache.poi.ss.usermodel.FillPatternType--- ---author:liusq---date:2024/7/30-----for:TV360X-1292 对象的属性为LocalDate、LocalDateTIme类型对象兼容--- autopoi\src\main\java\org\jeecgframework\poi\util\PoiPublicUtil.java autopoi\src\main\java\org\jeecgframework\poi\excel\imports\CellValueServer.java src\test\resources\templates\ExcelImportDateTest.xlsx src\test\java\TestDateEntity.java src\test\java\ImportExcelTest.java ---author:liusq---date:2024/7/30-----for:TV360X-1292 对象的属性为LocalDate、LocalDateTIme类型对象兼容--- ---author:chenrui---date:2024/8/1-----for:[issues/6925]xlsx模版导出图片--- src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java ---author:chenrui---date:2024/8/1-----for:[issues/6925]xlsx模版导出图片--- ---author:liusq---date:2024/8/7-----for:[issues/6925]autopoi通过word模板生成word时:三目、求长、常量、日期转换没起效果--- autopoi\src\main\java\org\jeecgframework\poi\util\PoiElUtil.java autopoi\src\main\java\org\jeecgframework\poi\util\PoiPublicUtil.java ---author:liusq---date:2024/8/7-----for:[issues#6096]autopoi通过word模板生成word时:三目、求长、常量、日期转换没起效果--- ---author:liusq---date:2024/9/3-----for:[issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式--- autopoi\src\main\java\org\jeecgframework\poi\excel\export\template\ExcelExportOfTemplateUtil.java autopoi\src\main\java\org\jeecgframework\poi\excel\entity\TemplateExportParams.java ---author:liusq---date:2024/9/3-----for:[issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式--- ---author:chenrui---date:2025/3/20-----for:[issues/7947]autopoi导入 报错Cell index must be >= 0 --- src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java ---author:chenrui---date:2025/3/20-----for:[issues/7947]autopoi导入 报错Cell index must be >= 0 --- ---author:liusq---date:2025/6/04-----for:[issues/8230] autopoi使用模板导出时,如果传入的map中存在值为null时会导致异常出错,导出失败--- autopoi\src\main\java\org\jeecgframework\poi\excel\export\template\ExcelExportOfTemplateUtil.java ---author:liusq---date:2024/6/04-----for:[issues/8230] autopoi使用模板导出时,如果传入的map中存在值为null时会导致异常出错,导出失败--- ---author:liusq---date:2024/6/04-----for:[issues/8148] autopoi使用模板导出时,如果在循环列后存在公式单元格,导出来后该单元格未空,公式没了--- autopoi\src\main\java\org\jeecgframework\poi\util\PoiExcelTempUtil.java ---author:liusq---date:2024/6/04-----for:[issues/8148] autopoi使用模板导出时,如果在循环列后存在公式单元格,导出来后该单元格未空,公式没了-- ---author:chenrui---date:2025/6/4-----for:[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248--- src/main/java/org/jeecgframework/poi/excel/annotation/Excel.java src/main/java/org/jeecgframework/poi/excel/export/base/ExcelExportBase.java ---author:chenrui---date:2025/6/4-----for:[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248--- ---author:liusq---date:2024/6/24-----for:[issues/8489] autopoi模板导出虽然公式单元格没问题了,但是正常的字符串单元格出错了 #8489-- autopoi\src\main\java\org\jeecgframework\poi\util\PoiExcelTempUtil.java ---author:liusq---date:2024/6/24-----for:[issues/8489] autopoi模板导出虽然公式单元格没问题了,但是正常的字符串单元格出错了 #8489-- ---author:chenrui---date:2025/7/16-----for:[issues/3943]导入excel时,标题区域的空行会导致下方列表数据被吞--- src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java ---author:chenrui---date:2025/7/16-----for:[issues/3943]导入excel时,标题区域的空行会导致下方列表数据被吞--- ---author:chenrui---date:2025/8/19-----for:[issues/8699]AutoPoi在使用@ExcelEntity当设置show=true并且该项为null时报错--- src/main/java/org/jeecgframework/poi/excel/export/base/ExportBase.java ---author:chenrui---date:2025/8/19-----for:[issues/8699]AutoPoi在使用@ExcelEntity当设置show=true并且该项为null时报错--- ---author:chenrui---date:2025/11/4-----for:[QQYUN-13964]演示系统数据量大,点击没反应--- autopoi/src/main/java/org/jeecgframework/poi/excel/export/ExcelBatchExportServer.java autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelExportUtil.java autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelExportServerEnhanced.java (+) autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgEntityExcelView.java autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgEntityExcelView.java docs/修改日志.log .gitignore ---author:chenrui---date:2025/11/4-----for:[QQYUN-13964]演示系统数据量大,点击没反应--- ---author:chenrui---date:2025/10/29-----for:[issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式--- src/main/java/org/jeecgframework/poi/consts/ImageScaleMode.java src/main/java/org/jeecgframework/poi/entity/ImageEntity.java src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java src/test/java/TestImageScale.java src/test/resources/templates/dakytot.jpeg src/test/resources/templates/testImageScale.xlsx ---author:chenrui---date:2025/10/29-----for:[issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式--- ---author:chenrui---date:2025/11/5-----for:[issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式-补提代码--- src/main/java/org/jeecgframework/poi/entity/ImageEntity.java ---author:chenrui---date:2025/11/5-----for:[issues/8892] AutoPoi ImageEntity建议添加scale属性,控制图片导出缩放模式-补提代码--- ---author:liusq---date:2025/12/11-----for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列--- autopoi\src\main\java\org\jeecgframework\poi\excel\annotation\Excel.java autopoi\src\main\java\org\jeecgframework\poi\excel\entity\params\ExcelExportEntity.java autopoi\src\main\java\org\jeecgframework\poi\excel\export\ExcelExportServer.java autopoi\src\main\java\org\jeecgframework\poi\excel\export\base\ExportBase.java autopoi\src\main\java\org\jeecgframework\poi\util\JsonParser.java ---author:liusq---date:2025/12/11-----for:JHHB-1212【AutoPoi】导出时,支持动态生成Excel的列--- -- author:sjlei---date:20251218--for: AutoPoi 导出动态列继承父列的 orderNum,不应动态增加,否则会导致列顺序错乱 --- autopoi/src/main/java/org/jeecgframework/poi/excel/export/base/ExportBase.java -- author:sjlei---date:20251218--for: AutoPoi 导出动态列继承父列的 orderNum,不应动态增加,否则会导致列顺序错乱 --- ================================================ FILE: install.bat ================================================ cmd /k mvn clean install -D skipTest ================================================ FILE: pom.xml ================================================ 4.0.0 org.jeecgframework autopoi-parent 2.0.4 pom autopoi-parent http://www.jeecg.com autopoi autopoi-spring-boot-2-starter autopoi-spring-boot-3-starter office 工具类 基于 poi The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt scm:git:https://github.com/zhangdaiscott/autopoi.git scm:git:https://github.com/zhangdaiscott/autopoi.git https://github.com/zhangdaiscott/autopoi jeecg jeecgos@163.com 2.0.4 5.4.1 2.9.1 32.1.3-jre 3.10 2.18.0 1.7.30 5.1.0.RELEASE org.apache.poi poi ${poi.version} org.apache.poi poi-ooxml ${poi.version} xerces xercesImpl ${xerces.version} true org.apache.poi poi-scratchpad ${poi.version} com.google.guava guava ${guava.version} org.apache.commons commons-lang3 ${commons-lang.version} commons-io commons-io ${commons-io.version} org.slf4j slf4j-api ${slf4j.version} org.slf4j slf4j-log4j12 ${slf4j.version} provided org.springframework spring-webmvc ${spring.version} true org.jeecgframework autopoi ${autopoi.version} jeecg maven-compiler-plugin 1.8 1.8 UTF-8 org.apache.maven.plugins maven-source-plugin 2.2.1 package jar-no-fork jeecg jeecg Repository http://maven.jeecg.com:8090/nexus/content/repositories/jeecg jeecg-snapshots jeecg Snapshot Repository http://maven.jeecg.com:8090/nexus/content/repositories/snapshots/ release maven-compiler-plugin 1.8 1.8 UTF-8 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 UTF-8 -Xdoclint:none org.apache.maven.plugins maven-gpg-plugin 1.6 sign-artifacts verify sign org.sonatype.central central-publishing-maven-plugin 0.7.0 true oss maven-compiler-plugin 3.8.1 1.8 1.8 UTF-8 org.apache.maven.plugins maven-surefire-plugin 3.0.0-M5 ================================================ FILE: 打包构建.md ================================================ # AutoPOI 打包构建指南 ## 📋 JDK 版本要求 不同模块对 JDK 版本有严格要求: | 模块 | 编译 JDK | 运行 JDK | 说明 | |------|---------|---------|------| | `autopoi` | **JDK 8+** | JDK 8+ | 核心模块,使用 JDK 8 API | | `autopoi-spring-boot-2-starter` | **JDK 8+** | JDK 8+ | Spring Boot 2.x 兼容 | | `autopoi-spring-boot-3-starter` | **JDK 17+** | JDK 17+ | Spring Boot 3.x 要求 | ## 🔧 关键配置:`` 参数 项目中使用了 `` 参数来严格控制 API 版本: ```xml org.apache.maven.plugins maven-compiler-plugin 8 17 ``` **这个参数的神奇之处:** - ✅ **即使你在 IDEA 中选择了 JDK 17,编译时也只能使用 JDK 8 的 API** - ✅ 如果代码中使用了 JDK 9+ 的 API(如 `List.of()`),编译会直接失败 - ✅ 生成的字节码与指定的 JDK 版本兼容 ## 🚀 推荐的打包方式 ### 方式一:使用 Maven 命令行(推荐) ```bash # 完整构建所有模块 mvn clean install # 跳过测试快速构建 mvn clean install -DskipTests # 只构建特定模块 cd autopoi mvn clean install ``` ### 方式二:在 IntelliJ IDEA 中打包 #### ⚠️ 重要提示 由于配置了 `` 参数,**你可以用任何 JDK 版本打包,但每个模块会强制使用对应版本的 API:** | IDEA 选择的 JDK | autopoi | spring-boot-2-starter | spring-boot-3-starter | |----------------|---------|---------------------|---------------------| | JDK 8 | ✅ JDK 8 API | ✅ JDK 8 API | ❌ 需要 JDK 17+ | | JDK 11 | ✅ JDK 8 API | ✅ JDK 8 API | ❌ 需要 JDK 17+ | | JDK 17 | ✅ JDK 8 API | ✅ JDK 8 API | ✅ JDK 17 API | **推荐:使用 JDK 17 打包所有模块!** #### IDEA 配置步骤 1. 打开 IDEA,点击右上角 Maven 面板 2. 选择 JDK 17(可以构建所有模块) 3. 双击 `Lifecycle` -> `install` 或 `package` ## 🧪 验证构建结果 ### 验证字节码版本 ```bash # Windows PowerShell javap -v autopoi\target\classes\org\jeecgframework\poi\excel\ExcelExportUtil.class | Select-String "major version" # 输出应该是: major version: 52 (JDK 8) javap -v autopoi-spring-boot-3-starter\target\classes\org\jeecgframework\poi\view\JeecgEntityExcelView.class | Select-String "major version" # 输出应该是: major version: 61 (JDK 17) ``` ### 检查依赖 ```bash # 验证没有 poi-ooxml-schemas mvn dependency:tree | findstr poi-ooxml-schemas # 应该没有输出 ``` ## ❌ 常见错误 ### 错误:使用了 JDK 9+ 的 API ```java // ❌ 错误:autopoi 模块中使用了 JDK 9 的 API List list = List.of("a", "b", "c"); // ✅ 正确:使用 JDK 8 兼容的写法 List list = Arrays.asList("a", "b", "c"); ``` **编译时会报错:** ``` [ERROR] cannot find symbol: method of(java.lang.String,java.lang.String,java.lang.String) ``` **解决方案:** 修改代码,使用 JDK 8 兼容的 API。 ## 📦 发布打包 ```bash # 打包到本地仓库 mvn clean install # 打包到 Maven 中央仓库(使用 release profile) mvn clean deploy -P release ``` ## 🎯 总结 1. **`` 参数是关键** - 无论用什么 JDK 编译,都只能使用指定版本的 API 2. **推荐使用 JDK 17 打包** - 可以一次性构建所有模块 3. **每个模块的 API 受到严格限制** - autopoi 和 spring-boot-2-starter 只能用 JDK 8 API ## 📚 参考文档 - [Maven Compiler Plugin - release parameter](https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#release)