├── README.md ├── easyexcel-plus-adapter ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── gongbo │ │ └── excel │ │ └── adapter │ │ └── easyexcel │ │ ├── EasyExcelAdapter.java │ │ ├── ImportReadListener.java │ │ ├── config │ │ └── EasyExcelAdapterConfig.java │ │ ├── converter │ │ ├── DefaultEnumConvert.java │ │ ├── ExcelValue.java │ │ ├── LocalDateConverter.java │ │ ├── LocalDateTimeConverter.java │ │ └── LocalTimeConverter.java │ │ └── overides │ │ └── ExcelWriteFillExecutor.java │ └── resources │ └── META-INF │ └── spring.factories ├── easyexcel-plus-boot-starter-test ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── gongbo │ │ └── excel │ │ └── example │ │ ├── ServerApplication.java │ │ ├── config │ │ ├── EasyExcelConfig.java │ │ ├── EasyExcelPlusConfig.java │ │ ├── SwaggerConfig.java │ │ └── WebConverters.java │ │ ├── constants │ │ └── GenderEnum.java │ │ ├── controller │ │ ├── ExportTestController.java │ │ └── ImportControllerTest.java │ │ ├── result │ │ ├── Result.java │ │ └── ResultCode.java │ │ └── view │ │ └── ExportDemoView.java │ └── resources │ ├── application.yml │ └── templates │ ├── template-formula.xls │ ├── template-import.xlsx │ ├── template-much-sheet.xlsx │ ├── template-simply.xlsx │ └── template-single-sheet.xlsx ├── easyexcel-plus-boot-starter ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── gongbo │ │ └── excel │ │ └── EasyExcelPlusConfiguration.java │ └── resources │ └── META-INF │ └── spring.factories ├── easyexcel-plus-common ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── gongbo │ └── excel │ └── common │ ├── adapter │ └── Adapter.java │ ├── enums │ └── ExcelType.java │ └── utils │ ├── BeanMap.java │ ├── CollectionUtil.java │ ├── ReflectUtil.java │ ├── ResponseUtils.java │ ├── StringPool.java │ ├── StringUtil.java │ ├── TemplateUtils.java │ ├── Times.java │ ├── Utils.java │ └── WebUtils.java ├── easyexcel-plus-export ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── gongbo │ └── excel │ └── export │ ├── adapter │ ├── ExportAdapter.java │ └── ExportAdapters.java │ ├── advise │ └── ExportAdvise.java │ ├── annotations │ ├── Export.java │ └── Exports.java │ ├── config │ ├── ExportConfig.java │ └── ExportProperties.java │ ├── constants │ └── ExportExcelType.java │ ├── core │ ├── ExportContextHolder.java │ ├── ExportHandlers.java │ ├── ExportProxy.java │ ├── lifecycle │ │ ├── AbstractExportLifeCycle.java │ │ ├── DefaultExportLifecycle.java │ │ └── ExportLifecycle.java │ └── resulthandler │ │ ├── DefaultResultHandler.java │ │ └── ResultHandler.java │ ├── custom │ ├── ExportDataConvert.java │ ├── FieldFilter.java │ ├── FileNameConvert.java │ └── defaults │ │ ├── DefaultExportDataConvert.java │ │ ├── DefaultFieldFilter.java │ │ └── DefaultFileNameConvert.java │ ├── entity │ ├── ExportContext.java │ ├── ExportFieldInfo.java │ └── ExportFillData.java │ ├── exception │ ├── ExportFailedException.java │ ├── FillKeyNotFoundException.java │ └── NotSupportExportException.java │ ├── param │ └── ExportParam.java │ └── utils │ ├── ExportFormulas.java │ └── ExportUtils.java ├── easyexcel-plus-import ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── gongbo │ └── excel │ └── imports │ ├── adapter │ ├── ImportAdapter.java │ └── ImportAdapters.java │ ├── advise │ └── ImportAdvise.java │ ├── annotations │ ├── Import.java │ └── ImportTarget.java │ ├── config │ ├── ImportConfig.java │ ├── ImportMappingJackson2HttpMessageConverter.java │ └── ImportProperties.java │ ├── core │ ├── ImportContextHolder.java │ ├── ImportProxy.java │ └── lifecycle │ │ ├── DefaultImportLifecycle.java │ │ └── ImportLifecycle.java │ ├── entity │ └── ImportContext.java │ ├── exception │ ├── ImportFailedException.java │ └── NotSupportImportException.java │ ├── param │ └── ImportParam.java │ └── utils │ └── ImportUtils.java ├── easyexcel-plus └── pom.xml ├── images └── img.png ├── img.png ├── img_1.png └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | # EasyExcelPlus 2 | 3 |

4 | 为简化开发工作、提高生产率而生 5 |

6 | 7 |

8 | 9 | maven 10 | 11 | 12 | 13 | code style 14 | 15 |

16 | 17 | # 简介 18 | 19 | 导入、导出通用工具包 - 简化导入,导出开发(注意,与easyexcel不是同一个作者,easyexcel作者最新项目为fastexcel) 20 | 21 | 22 | 技术讨论 QQ 群 : 779290098 23 | 24 | # 优点 | Advantages 25 | 26 | - **易使用**:只需极少的配置,便可接入 27 | - **更简洁**:通过在原有的查询(批量新增)接口上添加相关注解即可实现导出(导入)功能,不需要为导出单独写一份代码,直接复用相应的查询接口即可,开发更简单,维护更方便 28 | - **更规范**:查询与导出(批量新增与导入)使用同一请求地址,你只需要知道查询接口请求地址,便能进行导出 29 | - **损耗小**:对使用性能几乎没有影响 30 | - **可扩展**:默认已支持使用阿里EasyExcel进行导入、导出,你还可以通过添加适配器接入的方式使用其他导入、导出工具 31 | 32 | # Latest Version: [![Maven Central](https://img.shields.io/maven-central/v/io.github.gongbox/easyexcel-plus.svg)](https://search.maven.org/search?q=g:io.github.gongbox%20a:easyexcel-plus*) 33 | 34 | ``` xml 35 | 36 | io.github.gongbox 37 | easyexcel-plus-boot-starter 38 | Latest Version 39 | 40 | ``` 41 | ## 使用示例: 42 | 下面是一个普通查询接口: 43 | ```java 44 | @GetMapping(value = "test-normal") 45 | public Result> testNormal() { 46 | return Result.success(ExportDemoView.data()); 47 | } 48 | ``` 49 | 返回数据如下: 50 | > 演示地址:http://8.129.7.25/export/test-normal 51 | 52 | 若要实现导出excel,只需要在接口上增加注解@Export即可,如下所示: 53 | ```java 54 | @GetMapping(value = "test-normal") 55 | @Export 56 | public Result> testNormal() { 57 | return Result.success(ExportDemoView.data()); 58 | } 59 | ``` 60 | 添加该注解后,接口便同时支持查询、导出,不影响原有的查询。若要导出,则只需要添加请求参数export=excel即可,此时导出数据与查询结果一致,如下所示: 61 | > 演示地址(请复制地址到浏览器地址栏,按回车健访问):http://8.129.7.25/export/test-normal?export=excel 62 | 63 | ## 更多示例 64 | EasyExcelPlus支持多种多样的自定义配置,比如设置导出文件名、文件格式,模版导出,导出数据转换等等。 65 | 66 | 查看示例工程源码,请前往:https://github.com/gongbox/easyexcel-plus-example 67 | 68 | ### 环境说明 69 | - 模型类: 70 | ```java 71 | @Data 72 | @ColumnWidth(12) 73 | @ContentRowHeight(18) 74 | public class ExportDemoView { 75 | 76 | @ExcelProperty("文本") 77 | private String text = RandomUtil.randomString(8); 78 | 79 | @ExcelProperty("整数") 80 | private Integer integerValue = RandomUtil.randomInt(10000); 81 | 82 | @ExcelProperty("浮点数") 83 | private Float floatValue = (float) RandomUtil.randomDouble(-10000, 10000); 84 | 85 | @ExcelProperty("长浮点数") 86 | private Double doubleValue = RandomUtil.randomDouble(-10000, 10000); 87 | 88 | @ExcelProperty("定点数") 89 | private BigDecimal bigDecimal = RandomUtil.randomBigDecimal(BigDecimal.valueOf(10_000)); 90 | 91 | @ExcelProperty("日期") 92 | private LocalDate localDate = LocalDate.now(); 93 | 94 | @ExcelProperty("日期时间") 95 | @ColumnWidth(20) 96 | private LocalDateTime localDateTime = LocalDateTime.now(); 97 | 98 | @ExcelProperty("时间") 99 | @ColumnWidth(20) 100 | private Date date = new Date(); 101 | 102 | @ExcelProperty(value = "性别", converter = DefaultEnumConvert.class) 103 | private GenderEnum gender = GenderEnum.valueOf(RandomUtil.randomInt(3)); 104 | 105 | public static List data() { 106 | return Stream.generate(ExportDemoView::new) 107 | .limit(RandomUtil.randomInt(1, 20)) 108 | .collect(Collectors.toList()); 109 | } 110 | } 111 | ``` 112 | - 配置类: 113 | ```java 114 | @Configuration 115 | public class EasyExcelPlusConfig { 116 | 117 | @Bean 118 | public ResultHandler resultBuilder() { 119 | return new DefaultResultHandler() { 120 | @Override 121 | public Class resultClass() { 122 | return Result.class; 123 | } 124 | @Override 125 | public Object getResultData(Object result) { 126 | if (result instanceof Result) { 127 | return ((Result) result).getData(); 128 | } 129 | return super.getResultData(result); 130 | } 131 | }; 132 | } 133 | } 134 | ``` 135 | - 配置文件: 136 | ```yaml 137 | spring: 138 | application: 139 | name: export_demo 140 | 141 | server: 142 | port: 80 143 | #以下配置项可根据需要配置,或者不配置使用默认值即可 144 | easyexcel-plus: 145 | export: 146 | #默认导出Sheet名称,不配则取默认值:Sheet1 147 | default-sheet-name: Sheet1 148 | #模板文件路径,不配则取默认值:空 149 | template-dir: classpath:templates 150 | #默认导出文件格式,不配则取默认值:xlsx 151 | default-excel-type: xlsx 152 | #默认导出方式,不配则取默认值:easy_excel 153 | default-export-by: easy_excel 154 | import: 155 | #默认导入读取的Sheet名称,不配则取默认值:Sheet1 156 | default-sheet-name: Sheet1 157 | #模板文件路径,不配则取默认值:空 158 | template-dir: classpath:templates 159 | #默认导入方式,不配则取默认值:easy_excel 160 | default-import-by: easy_excel 161 | #读取excel超时时间(单位ms),不设置或设置为0时无读取时间限制 162 | read-timeout: 60000 163 | ``` 164 | ### 使用 165 | 166 | - **导出-设置导出文件名称** 167 | ```java 168 | @GetMapping(value = "test-fileName") 169 | @Export(fileName = "文件名称") 170 | public Result> testFilename() { 171 | return Result.success(ExportDemoView.data()); 172 | } 173 | ``` 174 | >演示地址:http://8.129.7.25/export/test-fileName?export=excel 175 | - **导出-动态设置文件名称** 176 | ```java 177 | @GetMapping(value = "test-fileName-convert") 178 | @Export(fileNameConvert = CustomFileNameConvert.class) 179 | public Result> testFileNameConvert() { 180 | return Result.success(ExportDemoView.data()); 181 | } 182 | 183 | public static class CustomFileNameConvert implements FileNameConvert { 184 | @Override 185 | public String apply(String fileName) { 186 | return LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); 187 | } 188 | } 189 | ``` 190 | >演示地址:http://8.129.7.25/export/test-fileName-convert?export=excel 191 | 192 | 或者 193 | ```java 194 | @GetMapping(value = "test-fileName-business") 195 | @Export 196 | public Result> testFileNameBusiness() { 197 | if (ExportContextHolder.isExportExcel()) { 198 | ExportContextHolder.getContext().setFileName("动态文件名称"); 199 | } 200 | return Result.success(ExportDemoView.data()); 201 | } 202 | ``` 203 | >演示地址:http://8.129.7.25/export/test-fileName-business?export=excel 204 | - **导出-固定Sheet名称** 205 | ```java 206 | @GetMapping(value = "test-sheetName") 207 | @Export(sheetName = "Sheet0") 208 | public Result> testSheetName() { 209 | return Result.success(ExportDemoView.data()); 210 | } 211 | ``` 212 | >演示地址:http://8.129.7.25/export/test-sheetName?export=excel 213 | - **导出-动态设置Sheet名称** 214 | ```java 215 | @GetMapping(value = "test-sheetName-business") 216 | @Export 217 | public Result> testSheetNameBusiness() { 218 | if (ExportContextHolder.isExportExcel()) { 219 | ExportContextHolder.getContext().setSheetName("业务中修改Sheet名称"); 220 | } 221 | return Result.success(ExportDemoView.data()); 222 | } 223 | ``` 224 | >演示地址:http://8.129.7.25/export/test-sheetName-business?export=excel 225 | - **导出到固定文件夹** 226 | ```java 227 | @GetMapping(value = "test-out-path") 228 | @Export(outputPath = "D:\\WorkDir\\temp\\file") 229 | public Result> testOutPath() { 230 | return Result.success(ExportDemoView.data()); 231 | } 232 | ``` 233 | >演示地址:http://8.129.7.25/export/test-out-path?export=excel 234 | - **导出-字段过滤** 235 | ```java 236 | @GetMapping(value = "test-filter") 237 | @Export(fieldFilter = CustomFieldFilter.class) 238 | public Result> testFilter() { 239 | return Result.success(ExportDemoView.data()); 240 | } 241 | 242 | public static class CustomFieldFilter implements FieldFilter { 243 | @Override 244 | public boolean predict(Field field) { 245 | return RandomUtil.randomBoolean(); 246 | } 247 | } 248 | ``` 249 | >演示地址:http://8.129.7.25/export/test-filter?export=excel 250 | - **导出-设置导出文件格式** 251 | ```java 252 | @GetMapping(value = "test-excelType") 253 | @Export(excelType = ExcelType.XLS) 254 | public Result> testExcelType() { 255 | return Result.success(ExportDemoView.data()); 256 | } 257 | ``` 258 | >演示地址:http://8.129.7.25/export/test-excelType?export=excel 259 | - **导出-数据转换** 260 | ```java 261 | @GetMapping(value = "test-dataConvert") 262 | @Export(dataConvert = CustomExportDataConvert.class) 263 | public Result> testDataConvert() { 264 | return Result.success(ExportDemoView.data()); 265 | } 266 | 267 | public static class CustomExportDataConvert implements ExportDataConvert { 268 | @Override 269 | public List convert(ExportContext exportContext, Object data) { 270 | Result responseEntity = (Result) data; 271 | List list = (List) responseEntity.getData(); 272 | for (int i = 0; i < 20; i++) { 273 | list.add(new ExportDemoView()); 274 | } 275 | return list; 276 | } 277 | } 278 | ``` 279 | >演示地址:http://8.129.7.25/export/test-dataConvert?export=excel 280 | - **导出-同一接口多种导出方式** 281 | ```java 282 | @GetMapping(value = "test-tag") 283 | @Export(tag = "xls", excelType = ExcelType.XLS) 284 | @Export(tag = "xlsx", excelType = ExcelType.XLSX) 285 | public Result> testTag() { 286 | return Result.success(ExportDemoView.data()); 287 | } 288 | ``` 289 | 同一接口可以添加多个注解,以实现支持多种导出,通过注解tag属性设置标签,导出时,需要使用参数export_tag指定标签。 290 | > 演示地址,导出XLS :http://8.129.7.25/export/test-tag?export=excel&export_tag=xls 291 | 292 | > 演示地址,导出XLSX:http://8.129.7.25/export/test-tag?export=excel&export_tag=xlsx 293 | - **导出-简单模版导出** 294 | ```java 295 | /** 296 | * 导出-简单模版导出 297 | */ 298 | @GetMapping(value = "test-template-simple") 299 | @Export(template = "template-simply.xlsx") 300 | public Result> testTemplateSimple() { 301 | return Result.success(ExportDemoView.data()); 302 | } 303 | ``` 304 | >演示地址:http://8.129.7.25/export/test-template-simple?export=excel 305 | - **导出-模版导出(单个Sheet)** 306 | ```java 307 | @GetMapping(value = "test-template-single-sheet") 308 | @Export(template = "template-single-sheet.xlsx", dataConvert = TemplateSingleSheetDataConvert.class) 309 | public Result> testTemplateSingleSheet() { 310 | return Result.success(ExportDemoView.data()); 311 | } 312 | 313 | public static class TemplateSingleSheetDataConvert implements ExportDataConvert { 314 | @Override 315 | public List convert(ExportContext exportContext, Object data) { 316 | Result responseEntity = (Result) data; 317 | 318 | ExportFillData exportFillData1 = ExportFillData.builder() 319 | .data(responseEntity.getData()) 320 | .build(); 321 | 322 | Map map = new HashMap<>(); 323 | map.put("name", "名称"); 324 | map.put("date", LocalDate.now().format(Times.Formatter.DEFAULT_DATE)); 325 | ExportFillData exportFillData2 = ExportFillData.builder() 326 | .data(map) 327 | .build(); 328 | 329 | return Lists.newArrayList(exportFillData1, exportFillData2); 330 | } 331 | } 332 | ``` 333 | >演示地址:http://8.129.7.25/export/test-template-single-sheet?export=excel 334 | - **导出-模版导出(多个Sheet)** 335 | ```java 336 | @GetMapping(value = "test-template-much-sheet") 337 | @Export(template = "template-much-sheet.xlsx", dataConvert = TemplateMuchSheetDataConvert.class) 338 | public Result> testTemplateMuchSheet() { 339 | return Result.success(ExportDemoView.data()); 340 | } 341 | 342 | public static class TemplateMuchSheetDataConvert implements ExportDataConvert { 343 | @Override 344 | public List convert(ExportContext exportContext, Object data) { 345 | Result responseEntity = (Result) data; 346 | 347 | Map map = new HashMap<>(); 348 | 349 | map.put("name", "名称"); 350 | map.put("date", LocalDate.now().format(Times.Formatter.DEFAULT_DATE)); 351 | ExportFillData exportFillData2 = ExportFillData.builder() 352 | .sheetName("Sheet1") 353 | .data(map) 354 | .build(); 355 | 356 | ExportFillData exportFillData1 = ExportFillData.builder() 357 | .sheetName("Sheet2") 358 | .data(responseEntity.getData()) 359 | .build(); 360 | 361 | return Lists.newArrayList(exportFillData1, exportFillData2); 362 | } 363 | } 364 | ``` 365 | >演示地址:http://8.129.7.25/export/test-template-much-sheet?export=excel 366 | - **导出-模版导出(公式)** 367 | ```java 368 | @GetMapping(value = "test-template-formula") 369 | @Export(template = "template-formula.xls", dataConvert = TemplateFormulaDataConvert.class) 370 | public Result> testTemplateFormula() { 371 | return Result.success(ExportDemoView.data()); 372 | } 373 | 374 | public static class TemplateFormulaDataConvert implements ExportDataConvert { 375 | @Override 376 | public List convert(ExportContext exportContext, Object data) { 377 | Result responseEntity = (Result) data; 378 | Collection list = (Collection) responseEntity.getData(); 379 | 380 | ExportFillData exportFillData1 = ExportFillData.builder() 381 | .fillConfig(FillConfig.builder().forceNewRow(true).build()) 382 | .data(new FillWrapper("data", list)) 383 | .build(); 384 | 385 | int start = 1; 386 | int end = start + (CollUtil.isEmpty(list) ? 0 : list.size() - 1); 387 | 388 | Map constantMap2 = new HashMap<>(); 389 | constantMap2.put("data_end", end); 390 | 391 | ExportFillData exportFillData2 = ExportFillData.builder() 392 | .data(constantMap2) 393 | .build(); 394 | 395 | return Lists.newArrayList(exportFillData1, exportFillData2); 396 | } 397 | } 398 | ``` 399 | >演示地址:http://8.129.7.25/export/test-template-formula?export=excel 400 | - **导出-无包装类** 401 | ```java 402 | /** 403 | * 导出-简单导出(直接返回数组) 404 | */ 405 | @GetMapping(value = "test-normal-array") 406 | @Export 407 | public ExportDemoView[] testNormalData() { 408 | return ExportDemoView.data().toArray(new ExportDemoView[0]); 409 | } 410 | 411 | /** 412 | * 导出-简单导出(直接返回集合) 413 | */ 414 | @GetMapping(value = "test-normal-list") 415 | @Export 416 | public List testNormalList() { 417 | return ExportDemoView.data(); 418 | } 419 | 420 | /** 421 | * 导出-简单导出(直接返回迭代器) 422 | */ 423 | @GetMapping(value = "test-normal-iterable") 424 | @Export 425 | public Iterable testNormalIterable() { 426 | return ExportDemoView.data(); 427 | } 428 | ``` 429 | >演示地址:http://8.129.7.25/export/test-normal-array?export=excel 430 | 431 | >演示地址:http://8.129.7.25/export/test-normal-list?export=excel 432 | 433 | >演示地址:http://8.129.7.25/export/test-normal-iterable?export=excel 434 | - **导入-模板下载** 435 | ```java 436 | @GetMapping(value = "test-template") 437 | @Import(modelClass = ExportDemoView.class) 438 | public void testTemplate() { 439 | } 440 | ``` 441 | >演示地址: 442 | > 导入-模版下载:http://8.129.7.25/import/test-template?import=template 443 | - **导入-自定义模版下载** 444 | ```java 445 | @GetMapping(value = "test-custom-template") 446 | @Import(modelClass = ExportDemoView.class, template = "template-import.xlsx", templateFilename = "自定义模板") 447 | public void testCustomTemplate() { 448 | } 449 | ``` 450 | >演示地址: 451 | > 导入-自定义模版下载:http://8.129.7.25/import/test-custom-template?import=template 452 | - **导入-导入** 453 | ```java 454 | @PostMapping(value = "test-import") 455 | @Import 456 | public Result testImport(@RequestBody(required = false) ExportDemoView[] param) { 457 | return Result.success(param); 458 | } 459 | ``` 460 | ![img_1.png](img_1.png) 461 | >演示地址: 462 | > 导入-模版下载:http://8.129.7.25/import/test-import?import=excel 463 | - **导入-模板下载、数据导入** 464 | ```java 465 | @RequestMapping(value = "test-import-template", method = {RequestMethod.GET, RequestMethod.POST}) 466 | @Import 467 | public Result testImportTemplate(@RequestBody(required = false) ExportDemoView[] param) { 468 | return Result.success(param); 469 | } 470 | ``` 471 | ![img_1.png](img_1.png) 472 | >演示地址: 导入-模版下载:http://8.129.7.25/import/test-import?import=template 473 | 474 | >演示地址: 导入-数组参数:http://8.129.7.25/import/test-import?import=excel 475 | 476 | # 期望 | Futures 477 | 478 | > 欢迎提出更好的意见,帮助完善 EasyExcelPlus 479 | 480 | # 版权 | License 481 | 482 | [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) 483 | 484 | # 关注我 | About Me 485 | 486 | [简书](https://www.jianshu.com/u/9d2985772d9a) 487 | 488 | -------------------------------------------------------------------------------- /easyexcel-plus-adapter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | easyexcel-plus-parent 7 | io.github.gongbox 8 | 2.4.1 9 | 10 | 4.0.0 11 | 12 | easyexcel-plus-adapter 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | io.github.gongbox 22 | easyexcel-plus-import 23 | ${project.parent.version} 24 | compile 25 | 26 | 27 | io.github.gongbox 28 | easyexcel-plus-export 29 | ${project.parent.version} 30 | compile 31 | 32 | 33 | io.github.gongbox 34 | easyexcel-plus-common 35 | ${project.parent.version} 36 | compile 37 | 38 | 39 | com.alibaba 40 | easyexcel 41 | compile 42 | 43 | 44 | org.springframework 45 | spring-aop 46 | compile 47 | 48 | 49 | org.aspectj 50 | aspectjweaver 51 | compile 52 | 53 | 54 | 55 | 56 | 57 | 58 | src/main/resources 59 | 60 | **/* 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /easyexcel-plus-adapter/src/main/java/com/gongbo/excel/adapter/easyexcel/EasyExcelAdapter.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.adapter.easyexcel; 2 | 3 | import com.alibaba.excel.EasyExcelFactory; 4 | import com.alibaba.excel.ExcelWriter; 5 | import com.alibaba.excel.annotation.ExcelProperty; 6 | import com.alibaba.excel.context.WriteContext; 7 | import com.alibaba.excel.enums.WriteTypeEnum; 8 | import com.alibaba.excel.exception.ExcelGenerateException; 9 | import com.alibaba.excel.read.builder.ExcelReaderBuilder; 10 | import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder; 11 | import com.alibaba.excel.support.ExcelTypeEnum; 12 | import com.alibaba.excel.write.builder.ExcelWriterBuilder; 13 | import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; 14 | import com.alibaba.excel.write.metadata.WriteSheet; 15 | import com.alibaba.excel.write.metadata.fill.FillConfig; 16 | import com.gongbo.excel.adapter.easyexcel.overides.ExcelWriteFillExecutor; 17 | import com.gongbo.excel.common.enums.ExcelType; 18 | import com.gongbo.excel.common.utils.CollectionUtil; 19 | import com.gongbo.excel.common.utils.ReflectUtil; 20 | import com.gongbo.excel.common.utils.StringPool; 21 | import com.gongbo.excel.common.utils.StringUtil; 22 | import com.gongbo.excel.export.adapter.ExportAdapter; 23 | import com.gongbo.excel.export.entity.ExportContext; 24 | import com.gongbo.excel.export.entity.ExportFieldInfo; 25 | import com.gongbo.excel.export.entity.ExportFillData; 26 | import com.gongbo.excel.export.exception.FillKeyNotFoundException; 27 | import com.gongbo.excel.imports.adapter.ImportAdapter; 28 | import com.gongbo.excel.imports.entity.ImportContext; 29 | import com.gongbo.excel.imports.utils.ImportUtils; 30 | import org.apache.poi.ss.usermodel.*; 31 | 32 | import java.io.IOException; 33 | import java.io.InputStream; 34 | import java.io.OutputStream; 35 | import java.lang.reflect.Field; 36 | import java.util.*; 37 | import java.util.concurrent.CompletableFuture; 38 | import java.util.concurrent.ExecutionException; 39 | import java.util.concurrent.TimeUnit; 40 | import java.util.concurrent.TimeoutException; 41 | import java.util.stream.Collectors; 42 | 43 | public class EasyExcelAdapter implements ExportAdapter, ImportAdapter { 44 | 45 | @Override 46 | public String name() { 47 | return "easy_excel"; 48 | } 49 | 50 | private ExcelTypeEnum convert(ExcelType excelType) { 51 | if (excelType.getValue().equals(ExcelTypeEnum.XLS.getValue())) { 52 | return ExcelTypeEnum.XLS; 53 | } else if (excelType.getValue().equals(ExcelTypeEnum.XLSX.getValue())) { 54 | return ExcelTypeEnum.XLSX; 55 | } else { 56 | throw new IllegalArgumentException(); 57 | } 58 | } 59 | 60 | @Override 61 | public Collection read(ImportContext importContext, InputStream inputStream) throws IOException, ExecutionException, InterruptedException, TimeoutException { 62 | //导入数据临时存放容器 63 | Collection container = ImportUtils.buildCollectionContainer(importContext.getTargetArgumentContainerClass()); 64 | 65 | CompletableFuture> completableFuture = new CompletableFuture<>(); 66 | 67 | //导入读取监听器 68 | ImportReadListener readListener = new ImportReadListener(container, 69 | (data, analysisContext) -> completableFuture.complete(data), 70 | (exception, analysisContext) -> completableFuture.completeExceptionally(exception)); 71 | 72 | //导入模型类 73 | Class modelClass = importContext.getTargetArgumentClass(); 74 | 75 | ExcelReaderBuilder readerBuilder = EasyExcelFactory.read(inputStream, modelClass, readListener); 76 | 77 | ExcelReaderSheetBuilder excelReaderSheetBuilder; 78 | //设置读取的sheet 79 | if (importContext.getSheetNo() != null && importContext.getSheetNo() >= 0) { 80 | excelReaderSheetBuilder = readerBuilder.sheet(importContext.getSheetNo()); 81 | } else if (StringUtil.isNotEmpty(importContext.getSheetName())) { 82 | excelReaderSheetBuilder = readerBuilder.sheet(importContext.getSheetName()); 83 | } else { 84 | excelReaderSheetBuilder = readerBuilder.sheet(0); 85 | } 86 | 87 | //读取 88 | excelReaderSheetBuilder.doRead(); 89 | 90 | //等待数据解析完成 91 | Integer readTimeout = importContext.getImportProperties().getReadTimeout(); 92 | if (readTimeout == null || readTimeout <= 0) { 93 | return completableFuture.get(); 94 | } 95 | return completableFuture.get(readTimeout, TimeUnit.MILLISECONDS); 96 | } 97 | 98 | @Override 99 | public void responseTemplate(ImportContext importContext, OutputStream outputStream) throws IOException { 100 | //生成导入模板 101 | ExcelWriterBuilder excelWriterBuilder = EasyExcelFactory.write(outputStream, importContext.getTargetArgumentClass()); 102 | 103 | excelWriterBuilder.sheet(importContext.getSheetNo(), importContext.getSheetName()) 104 | .doWrite((Collection) null); 105 | } 106 | 107 | @Override 108 | public void export(ExportContext exportContext, List data, OutputStream outputStream) throws IOException { 109 | if (data == null) { 110 | data = Collections.emptyList(); 111 | } 112 | ExcelWriterBuilder excelWriterBuilder = EasyExcelFactory.write(outputStream, exportContext.getModel()); 113 | 114 | //需要保留的字段名 115 | if (CollectionUtil.isNotEmpty(exportContext.getFieldInfos())) { 116 | Set includeColumns = exportContext.getFieldInfos().stream() 117 | .map(ExportFieldInfo::getFieldName) 118 | .collect(Collectors.toSet()); 119 | excelWriterBuilder = excelWriterBuilder.includeColumnFiledNames(includeColumns); 120 | } 121 | 122 | //设置导出文件格式 123 | excelWriterBuilder = excelWriterBuilder.excelType(convert(exportContext.getExcelType())); 124 | 125 | ExcelWriterSheetBuilder excelWriterSheetBuilder = excelWriterBuilder 126 | .sheet(StringUtil.firstNotEmpty(exportContext.getSheetName(), exportContext.getExportProperties().getDefaultSheetName())); 127 | excelWriterSheetBuilder.doWrite(data); 128 | } 129 | 130 | @Override 131 | public void export(ExportContext exportContext, InputStream templateInputStream, List data, OutputStream outputStream) { 132 | if (data == null) { 133 | data = Collections.emptyList(); 134 | } 135 | ExcelWriterBuilder excelWriterBuilder = EasyExcelFactory.write(outputStream, exportContext.getModel()); 136 | 137 | excelWriterBuilder = excelWriterBuilder.withTemplate(templateInputStream); 138 | 139 | //需要保留的字段名 140 | if (CollectionUtil.isNotEmpty(exportContext.getFieldInfos())) { 141 | Set includeColumns = exportContext.getFieldInfos().stream() 142 | .map(ExportFieldInfo::getFieldName) 143 | .collect(Collectors.toSet()); 144 | excelWriterBuilder = excelWriterBuilder.includeColumnFiledNames(includeColumns); 145 | } 146 | 147 | //设置导出文件格式 148 | excelWriterBuilder = excelWriterBuilder.excelType(convert(exportContext.getExcelType())); 149 | 150 | ExcelWriter excelWriter = excelWriterBuilder 151 | .build(); 152 | 153 | //获取填充数据 154 | List exportFillDataList = exportContext.listExportFillData(); 155 | 156 | //data是否是填充数据标志(判断依据:data不为空且data中没有FillEntity类型数据) 157 | boolean dataFillFlag; 158 | 159 | //合并数据与填充数据 160 | if (dataFillFlag = CollectionUtil.isNotEmpty(data)) { 161 | for (Object item : data) { 162 | if (item instanceof ExportFillData) { 163 | exportFillDataList.add((ExportFillData) item); 164 | dataFillFlag = false; 165 | } 166 | } 167 | } 168 | 169 | //使用自己的执行器 170 | ExcelWriteFillExecutor excelWriteFillExecutor = new ExcelWriteFillExecutor(excelWriter.writeContext()); 171 | 172 | //data是填充数据 173 | if (dataFillFlag) { 174 | WriteSheet writeSheet = new ExcelWriterSheetBuilder(excelWriter) 175 | .sheetName(StringUtil.firstNotEmpty(exportContext.getSheetName(), exportContext.getExportProperties().getDefaultSheetName())) 176 | .build(); 177 | 178 | //填充 179 | fill(excelWriter.writeContext(), excelWriteFillExecutor, data, null, writeSheet, false); 180 | } 181 | 182 | //填充数据 183 | for (ExportFillData exportFillData : exportFillDataList) { 184 | if (exportFillData.isFillAllSheet()) { 185 | //获取所有sheet个数 186 | int numberOfSheets = excelWriter.writeContext() 187 | .writeWorkbookHolder() 188 | .getWorkbook() 189 | .getNumberOfSheets(); 190 | 191 | //遍历所有sheet填充 192 | for (int sheetNo = 0; sheetNo < numberOfSheets; sheetNo++) { 193 | WriteSheet writeSheet = new ExcelWriterSheetBuilder(excelWriter) 194 | .sheetNo(sheetNo) 195 | .build(); 196 | 197 | //填充 198 | fill(excelWriter.writeContext(), excelWriteFillExecutor, exportFillData.getData(), (FillConfig) exportFillData.getFillConfig(), writeSheet, true); 199 | } 200 | } else { 201 | WriteSheet writeSheet = new ExcelWriterSheetBuilder(excelWriter) 202 | .sheetNo(exportFillData.getSheetNo()) 203 | .sheetName(exportFillData.getSheetName()) 204 | .build(); 205 | //填充 206 | fill(excelWriter.writeContext(), excelWriteFillExecutor, exportFillData.getData(), (FillConfig) exportFillData.getFillConfig(), writeSheet, false); 207 | } 208 | } 209 | 210 | //公式填充 211 | if (exportContext.isFormula()) { 212 | doFormula(exportContext, excelWriter.writeContext().writeWorkbookHolder().getWorkbook()); 213 | } 214 | 215 | excelWriter.finish(); 216 | } 217 | 218 | //公式填充 219 | private void doFormula(ExportContext exportContext, Workbook workbook) { 220 | workbook.setForceFormulaRecalculation(true); 221 | FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); 222 | for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) { 223 | Sheet sheet = workbook.getSheetAt(sheetNum); 224 | for (Row r : sheet) { 225 | for (Cell c : r) { 226 | if (c != null && c.getCellTypeEnum() == CellType.STRING) { 227 | String cell = c.getStringCellValue(); 228 | if (StringUtil.isNotEmpty(cell)) { 229 | if (cell.startsWith(StringPool.EQUALS)) { 230 | c.setCellFormula(cell.substring(1)); 231 | evaluator.evaluate(c); 232 | } else if (cell.startsWith(exportContext.getExportProperties().getFormulaPrefix())) { 233 | c.setCellFormula(cell.substring(exportContext.getExportProperties().getFormulaPrefix().length())); 234 | evaluator.evaluate(c); 235 | } 236 | } 237 | } 238 | } 239 | } 240 | } 241 | } 242 | 243 | public void fill(WriteContext context, ExcelWriteFillExecutor excelWriteFillExecutor, 244 | Object data, FillConfig fillConfig, WriteSheet writeSheet, 245 | boolean ignoreFillKeyNotFound) { 246 | try { 247 | if (context.writeWorkbookHolder().getTempTemplateInputStream() == null) { 248 | throw new ExcelGenerateException("Calling the 'fill' method must use a template."); 249 | } 250 | context.currentSheet(writeSheet, WriteTypeEnum.FILL); 251 | 252 | excelWriteFillExecutor.fill(data, fillConfig); 253 | } catch (FillKeyNotFoundException e) { 254 | if (!ignoreFillKeyNotFound) { 255 | context.finish(true); 256 | throw e; 257 | } 258 | } catch (RuntimeException e) { 259 | context.finish(true); 260 | throw e; 261 | } catch (Exception e) { 262 | context.finish(true); 263 | throw new ExcelGenerateException(e); 264 | } 265 | } 266 | 267 | @Override 268 | public List findExportFieldInfos(Class clazz) { 269 | return ReflectUtil.getFields(clazz, true).stream() 270 | .map(this::findExportFieldInfo) 271 | .filter(Objects::nonNull) 272 | .collect(Collectors.toList()); 273 | } 274 | 275 | public ExportFieldInfo findExportFieldInfo(Field field) { 276 | ExcelProperty exportField = field.getAnnotation(ExcelProperty.class); 277 | if (exportField != null) { 278 | String name = null; 279 | String[] value = exportField.value(); 280 | //取最后一个作为字段名 281 | if (value.length > 0) { 282 | name = String.join(".", value); 283 | } 284 | 285 | return ExportFieldInfo.builder() 286 | .fieldName(field.getName()) 287 | .name(name) 288 | .order(exportField.index()) 289 | .build(); 290 | } 291 | return null; 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /easyexcel-plus-adapter/src/main/java/com/gongbo/excel/adapter/easyexcel/ImportReadListener.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.adapter.easyexcel; 2 | 3 | import com.alibaba.excel.context.AnalysisContext; 4 | import com.alibaba.excel.metadata.CellExtra; 5 | import com.alibaba.excel.read.listener.ReadListener; 6 | 7 | import java.util.Collection; 8 | import java.util.function.BiConsumer; 9 | 10 | /** 11 | * 导入数据读取 12 | */ 13 | class ImportReadListener implements ReadListener { 14 | 15 | /** 16 | * 导入数据 17 | */ 18 | private Collection list; 19 | 20 | /** 21 | * 导入完成回调 22 | */ 23 | private BiConsumer, AnalysisContext> completedConsumer; 24 | 25 | private BiConsumer exceptionConsumer; 26 | 27 | public ImportReadListener(Collection list, 28 | BiConsumer, AnalysisContext> completedConsumer, 29 | BiConsumer exceptionConsumer) { 30 | this.list = list; 31 | this.completedConsumer = completedConsumer; 32 | this.exceptionConsumer = exceptionConsumer; 33 | } 34 | 35 | @Override 36 | public void onException(Exception exception, AnalysisContext context) throws Exception { 37 | if (exceptionConsumer != null) { 38 | exceptionConsumer.accept(exception, context); 39 | } 40 | } 41 | 42 | @Override 43 | public void invoke(Object data, AnalysisContext context) { 44 | list.add(data); 45 | } 46 | 47 | @Override 48 | public void extra(CellExtra extra, AnalysisContext context) { 49 | 50 | } 51 | 52 | @Override 53 | public void doAfterAllAnalysed(AnalysisContext context) { 54 | if (completedConsumer != null) { 55 | completedConsumer.accept(list, context); 56 | } 57 | } 58 | 59 | @Override 60 | public boolean hasNext(AnalysisContext context) { 61 | return true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /easyexcel-plus-adapter/src/main/java/com/gongbo/excel/adapter/easyexcel/config/EasyExcelAdapterConfig.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.adapter.easyexcel.config; 2 | 3 | import com.gongbo.excel.adapter.easyexcel.EasyExcelAdapter; 4 | import com.gongbo.excel.export.adapter.ExportAdapters; 5 | import com.gongbo.excel.imports.adapter.ImportAdapters; 6 | import lombok.NoArgsConstructor; 7 | 8 | @NoArgsConstructor 9 | public class EasyExcelAdapterConfig { 10 | 11 | static { 12 | EasyExcelAdapter adapter = new EasyExcelAdapter(); 13 | ExportAdapters.addAdapter(adapter); 14 | ImportAdapters.addAdapter(adapter); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /easyexcel-plus-adapter/src/main/java/com/gongbo/excel/adapter/easyexcel/converter/DefaultEnumConvert.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.adapter.easyexcel.converter; 2 | 3 | import com.alibaba.excel.converters.Converter; 4 | import com.alibaba.excel.enums.CellDataTypeEnum; 5 | import com.alibaba.excel.metadata.GlobalConfiguration; 6 | import com.alibaba.excel.metadata.data.ReadCellData; 7 | import com.alibaba.excel.metadata.data.WriteCellData; 8 | import com.alibaba.excel.metadata.property.ExcelContentProperty; 9 | 10 | import java.lang.reflect.Field; 11 | import java.util.Arrays; 12 | import java.util.Objects; 13 | import java.util.Optional; 14 | 15 | public class DefaultEnumConvert implements Converter { 16 | @Override 17 | public Class supportJavaTypeKey() { 18 | return Object.class; 19 | } 20 | 21 | @Override 22 | public CellDataTypeEnum supportExcelTypeKey() { 23 | return CellDataTypeEnum.STRING; 24 | } 25 | 26 | @Override 27 | public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception { 28 | String stringValue = Optional.ofNullable(cellData.getStringValue()).map(String::trim).orElse(""); 29 | 30 | //获取枚举类型 31 | Class type = excelContentProperty.getField().getType(); 32 | 33 | //查找对应的值字段 34 | Field field = Arrays.stream(type.getDeclaredFields()) 35 | .filter(f -> { 36 | ExcelValue excelValue = f.getAnnotation(ExcelValue.class); 37 | if (excelValue == null) { 38 | return false; 39 | } 40 | return excelValue.value() == ExcelValue.Support.READ || excelValue.value() == ExcelValue.Support.ALL; 41 | }).findAny() 42 | .orElseThrow(() -> new IllegalArgumentException("在枚举类上没有找到支持输入的ExcelValue注解")); 43 | //允许访问私有属性 44 | field.setAccessible(true); 45 | for (Object enumConstant : type.getEnumConstants()) { 46 | Object value = field.get(enumConstant); 47 | if (Objects.equals(String.valueOf(value), stringValue)) { 48 | return enumConstant; 49 | } 50 | } 51 | throw new IllegalStateException("没有匹配到对应的枚举值:" + stringValue); 52 | } 53 | 54 | @Override 55 | public WriteCellData convertToExcelData(Object o, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception { 56 | Class type = excelContentProperty.getField().getType(); 57 | Field[] declaredFields = type.getDeclaredFields(); 58 | 59 | Field field = Arrays.stream(declaredFields) 60 | .filter(f -> { 61 | ExcelValue excelValue = f.getAnnotation(ExcelValue.class); 62 | if (excelValue == null) { 63 | return false; 64 | } 65 | return excelValue.value() == ExcelValue.Support.WRITE || excelValue.value() == ExcelValue.Support.ALL; 66 | 67 | }).findAny() 68 | .orElseThrow(() -> new IllegalArgumentException("在枚举类上没有找到支持输出的ExcelValue注解")); 69 | 70 | //允许访问私有属性 71 | field.setAccessible(true); 72 | Object value = field.get(o); 73 | return new WriteCellData<>(String.valueOf(value)); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /easyexcel-plus-adapter/src/main/java/com/gongbo/excel/adapter/easyexcel/converter/ExcelValue.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.adapter.easyexcel.converter; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Documented 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.FIELD}) 8 | public @interface ExcelValue { 9 | 10 | /** 11 | * 支持方式 12 | */ 13 | Support value() default Support.ALL; 14 | 15 | enum Support { 16 | /** 17 | * 读取 18 | */ 19 | READ, 20 | /** 21 | * 输出 22 | */ 23 | WRITE, 24 | /** 25 | * 读取、输出 26 | */ 27 | ALL 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /easyexcel-plus-adapter/src/main/java/com/gongbo/excel/adapter/easyexcel/converter/LocalDateConverter.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.adapter.easyexcel.converter; 2 | 3 | 4 | import com.alibaba.excel.converters.Converter; 5 | import com.alibaba.excel.enums.CellDataTypeEnum; 6 | import com.alibaba.excel.metadata.GlobalConfiguration; 7 | import com.alibaba.excel.metadata.data.ReadCellData; 8 | import com.alibaba.excel.metadata.data.WriteCellData; 9 | import com.alibaba.excel.metadata.property.ExcelContentProperty; 10 | import com.alibaba.excel.util.DateUtils; 11 | 12 | import java.time.LocalDate; 13 | import java.time.format.DateTimeFormatter; 14 | 15 | public class LocalDateConverter implements Converter { 16 | @Override 17 | public Class supportJavaTypeKey() { 18 | return LocalDate.class; 19 | } 20 | 21 | @Override 22 | public CellDataTypeEnum supportExcelTypeKey() { 23 | return CellDataTypeEnum.STRING; 24 | } 25 | 26 | @Override 27 | public LocalDate convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { 28 | if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { 29 | return LocalDate.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_10)); 30 | } else { 31 | return LocalDate.parse(cellData.getStringValue(), 32 | DateTimeFormatter.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat())); 33 | } 34 | } 35 | 36 | @Override 37 | public WriteCellData convertToExcelData(LocalDate value, 38 | ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { 39 | if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { 40 | return new WriteCellData<>(value.format(DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_10))); 41 | } else { 42 | return new WriteCellData<>(value.format(DateTimeFormatter.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat()))); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /easyexcel-plus-adapter/src/main/java/com/gongbo/excel/adapter/easyexcel/converter/LocalDateTimeConverter.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.adapter.easyexcel.converter; 2 | 3 | 4 | import com.alibaba.excel.converters.Converter; 5 | import com.alibaba.excel.enums.CellDataTypeEnum; 6 | import com.alibaba.excel.metadata.GlobalConfiguration; 7 | import com.alibaba.excel.metadata.data.ReadCellData; 8 | import com.alibaba.excel.metadata.data.WriteCellData; 9 | import com.alibaba.excel.metadata.property.ExcelContentProperty; 10 | import com.alibaba.excel.util.DateUtils; 11 | 12 | import java.time.LocalDateTime; 13 | import java.time.format.DateTimeFormatter; 14 | 15 | public class LocalDateTimeConverter implements Converter { 16 | @Override 17 | public Class supportJavaTypeKey() { 18 | return LocalDateTime.class; 19 | } 20 | 21 | @Override 22 | public CellDataTypeEnum supportExcelTypeKey() { 23 | return CellDataTypeEnum.STRING; 24 | } 25 | 26 | @Override 27 | public LocalDateTime convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { 28 | if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { 29 | return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_19)); 30 | } else { 31 | return LocalDateTime.parse(cellData.getStringValue(), 32 | DateTimeFormatter.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat())); 33 | } 34 | } 35 | 36 | @Override 37 | public WriteCellData convertToExcelData(LocalDateTime value, 38 | ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { 39 | if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { 40 | return new WriteCellData<>(value.format(DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_19))); 41 | } else { 42 | return new WriteCellData<>(value.format(DateTimeFormatter.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat()))); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /easyexcel-plus-adapter/src/main/java/com/gongbo/excel/adapter/easyexcel/converter/LocalTimeConverter.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.adapter.easyexcel.converter; 2 | 3 | 4 | import com.alibaba.excel.converters.Converter; 5 | import com.alibaba.excel.enums.CellDataTypeEnum; 6 | import com.alibaba.excel.metadata.GlobalConfiguration; 7 | import com.alibaba.excel.metadata.data.ReadCellData; 8 | import com.alibaba.excel.metadata.data.WriteCellData; 9 | import com.alibaba.excel.metadata.property.ExcelContentProperty; 10 | import com.gongbo.excel.common.utils.Times; 11 | 12 | import java.time.LocalTime; 13 | import java.time.format.DateTimeFormatter; 14 | 15 | public class LocalTimeConverter implements Converter { 16 | @Override 17 | public Class supportJavaTypeKey() { 18 | return LocalTime.class; 19 | } 20 | 21 | @Override 22 | public CellDataTypeEnum supportExcelTypeKey() { 23 | return CellDataTypeEnum.STRING; 24 | } 25 | 26 | @Override 27 | public LocalTime convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { 28 | if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { 29 | return LocalTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern(Times.Pattern.DEFAULT_TIME)); 30 | } else { 31 | return LocalTime.parse(cellData.getStringValue(), 32 | DateTimeFormatter.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat())); 33 | } 34 | } 35 | 36 | @Override 37 | public WriteCellData convertToExcelData(LocalTime value, 38 | ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { 39 | if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { 40 | return new WriteCellData<>(value.format(DateTimeFormatter.ofPattern(Times.Pattern.DEFAULT_TIME))); 41 | } else { 42 | return new WriteCellData<>(value.format(DateTimeFormatter.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat()))); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /easyexcel-plus-adapter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.gongbo.excel.adapter.easyexcel.config.EasyExcelAdapterConfig -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | easyexcel-plus-parent 7 | io.github.gongbox 8 | 2.4.1 9 | 10 | 4.0.0 11 | 12 | easyexcel-plus-boot-starter-test 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | io.github.gongbox 22 | easyexcel-plus-boot-starter 23 | ${project.parent.version} 24 | compile 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | compile 30 | 31 | 32 | io.springfox 33 | springfox-swagger2 34 | compile 35 | 36 | 37 | io.springfox 38 | springfox-swagger-ui 39 | compile 40 | 41 | 42 | cn.hutool 43 | hutool-core 44 | compile 45 | 46 | 47 | 48 | 49 | 50 | easyexcel-plus-demo 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-maven-plugin 55 | 56 | com.gongbo.excel.example.ServerApplication 57 | 58 | 59 | 60 | 61 | repackage 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | src/main/resources 70 | 71 | **/* 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/ServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ServerApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ServerApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/config/EasyExcelConfig.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example.config; 2 | 3 | import com.alibaba.excel.converters.Converter; 4 | import com.alibaba.excel.converters.ConverterKeyBuild; 5 | import com.gongbo.excel.adapter.easyexcel.converter.LocalDateConverter; 6 | import com.gongbo.excel.adapter.easyexcel.converter.LocalDateTimeConverter; 7 | import com.gongbo.excel.adapter.easyexcel.converter.LocalTimeConverter; 8 | import lombok.NoArgsConstructor; 9 | import org.springframework.beans.factory.InitializingBean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | import static com.alibaba.excel.converters.DefaultConverterLoader.loadAllConverter; 13 | import static com.alibaba.excel.converters.DefaultConverterLoader.loadDefaultWriteConverter; 14 | 15 | @NoArgsConstructor 16 | @Configuration 17 | public class EasyExcelConfig implements InitializingBean { 18 | 19 | 20 | private static void putAllConverter(Converter converter) { 21 | loadAllConverter().put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()), 22 | converter); 23 | } 24 | 25 | private static void putWriteConverter(Converter converter) { 26 | loadDefaultWriteConverter().put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter); 27 | } 28 | 29 | @Override 30 | public void afterPropertiesSet() throws Exception { 31 | LocalDateTimeConverter localDateTimeConverter = new LocalDateTimeConverter(); 32 | LocalDateConverter localDateConverter = new LocalDateConverter(); 33 | LocalTimeConverter localTimeConverter = new LocalTimeConverter(); 34 | 35 | putWriteConverter(localDateConverter); 36 | putWriteConverter(localDateTimeConverter); 37 | putWriteConverter(localTimeConverter); 38 | 39 | putAllConverter(localDateConverter); 40 | putAllConverter(localDateTimeConverter); 41 | putAllConverter(localTimeConverter); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/config/EasyExcelPlusConfig.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example.config; 2 | 3 | import com.gongbo.excel.example.result.Result; 4 | import com.gongbo.excel.export.core.resulthandler.DefaultResultHandler; 5 | import com.gongbo.excel.export.core.resulthandler.ResultHandler; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | public class EasyExcelPlusConfig { 11 | 12 | @Bean 13 | public ResultHandler resultBuilder() { 14 | return new DefaultResultHandler() { 15 | @Override 16 | public Class resultClass() { 17 | return Result.class; 18 | } 19 | 20 | @Override 21 | public Object getResultData(Object result) { 22 | if (result instanceof Result) { 23 | return ((Result) result).getData(); 24 | } 25 | return super.getResultData(result); 26 | } 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.ApiInfoBuilder; 6 | import springfox.documentation.builders.RequestHandlerSelectors; 7 | import springfox.documentation.service.ApiInfo; 8 | import springfox.documentation.spi.DocumentationType; 9 | import springfox.documentation.spring.web.plugins.Docket; 10 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 11 | 12 | import java.time.*; 13 | import java.util.Date; 14 | 15 | @Configuration 16 | @EnableSwagger2 17 | public class SwaggerConfig { 18 | 19 | @Bean 20 | public Docket webApi() { 21 | return new Docket(DocumentationType.SWAGGER_2) 22 | .directModelSubstitute(LocalDateTime.class, Date.class) 23 | .directModelSubstitute(LocalDate.class, Date.class) 24 | .directModelSubstitute(LocalTime.class, Date.class) 25 | .directModelSubstitute(Year.class, Integer.class) 26 | .directModelSubstitute(Month.class, Integer.class) 27 | .directModelSubstitute(YearMonth.class, String.class) 28 | .groupName("EasyExcelPlus") 29 | .apiInfo(apiInfo()) 30 | .select() 31 | .apis(RequestHandlerSelectors.basePackage("com.gongbo.excel.example.controller")) 32 | .build(); 33 | } 34 | 35 | 36 | private ApiInfo apiInfo() { 37 | return new ApiInfoBuilder() 38 | .title("EasyExcelPlus") 39 | .description("EasyExcelPlus接口文档") 40 | .version("1.0") 41 | .build(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/config/WebConverters.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example.config; 2 | 3 | import com.gongbo.excel.common.utils.StringUtil; 4 | import com.gongbo.excel.common.utils.Times; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.core.convert.converter.Converter; 8 | 9 | import java.time.*; 10 | import java.util.Optional; 11 | 12 | @Configuration 13 | public class WebConverters { 14 | 15 | @Bean 16 | public Converter localDateConvert() { 17 | return new Converter() { 18 | @Override 19 | public LocalDate convert(String source) { 20 | return Optional.of(source) 21 | .filter(StringUtil::isNotEmpty) 22 | .map(v -> LocalDate.parse(v, Times.Formatter.DEFAULT_DATE)) 23 | .orElse(null); 24 | } 25 | }; 26 | } 27 | 28 | @Bean 29 | public Converter localTimeConvert() { 30 | return new Converter() { 31 | @Override 32 | public LocalTime convert(String source) { 33 | return Optional.of(source) 34 | .filter(StringUtil::isNotEmpty) 35 | .map(v -> LocalTime.parse(v, Times.Formatter.DEFAULT_TIME)) 36 | .orElse(null); 37 | } 38 | }; 39 | } 40 | 41 | @Bean 42 | public Converter localDateTimeConvert() { 43 | return new Converter() { 44 | @Override 45 | public LocalDateTime convert(String source) { 46 | return Optional.of(source) 47 | .filter(StringUtil::isNotEmpty) 48 | .map(v -> LocalDateTime.parse(v, Times.Formatter.DEFAULT_DATE_TIME)) 49 | .orElse(null); 50 | } 51 | }; 52 | } 53 | 54 | @Bean 55 | public Converter yearMonthConverter() { 56 | return new Converter() { 57 | @Override 58 | public YearMonth convert(String source) { 59 | return Optional.of(source) 60 | .filter(StringUtil::isNotEmpty) 61 | .map(v -> YearMonth.parse(v, Times.Formatter.DEFAULT_YEAR_MONTH)) 62 | .orElse(null); 63 | } 64 | }; 65 | } 66 | 67 | @Bean 68 | public Converter yearConverter() { 69 | return new Converter() { 70 | @Override 71 | public Year convert(String source) { 72 | return Optional.of(source) 73 | .filter(StringUtil::isNotEmpty) 74 | .map(v -> Year.parse(v, Times.Formatter.DEFAULT_YEAR)) 75 | .orElse(null); 76 | } 77 | }; 78 | } 79 | 80 | @Bean 81 | public Converter monthConverter() { 82 | return new Converter() { 83 | @Override 84 | public Month convert(String source) { 85 | return Optional.of(source) 86 | .filter(StringUtil::isNotEmpty) 87 | .map(Integer::parseInt) 88 | .map(Month::of) 89 | .orElse(null); 90 | } 91 | }; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/constants/GenderEnum.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example.constants; 2 | 3 | import com.gongbo.excel.adapter.easyexcel.converter.ExcelValue; 4 | import lombok.Getter; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Getter 8 | @RequiredArgsConstructor 9 | public enum GenderEnum { 10 | UNKNOWN(0, "未知"), 11 | MAN(1, "男"), 12 | WOMAN(2, "女"); 13 | 14 | @ExcelValue(ExcelValue.Support.READ) 15 | private final Integer value; 16 | 17 | @ExcelValue(ExcelValue.Support.WRITE) 18 | private final String name; 19 | 20 | public static GenderEnum valueOf(int value) { 21 | for (GenderEnum genderEnum : values()) { 22 | if (genderEnum.value == value) { 23 | return genderEnum; 24 | } 25 | } 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/controller/ExportTestController.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example.controller; 2 | 3 | import cn.hutool.core.collection.CollUtil; 4 | import cn.hutool.core.util.RandomUtil; 5 | import com.alibaba.excel.write.metadata.fill.FillConfig; 6 | import com.alibaba.excel.write.metadata.fill.FillWrapper; 7 | import com.gongbo.excel.common.utils.Times; 8 | import com.gongbo.excel.example.result.Result; 9 | import com.gongbo.excel.example.view.ExportDemoView; 10 | import com.gongbo.excel.export.annotations.Export; 11 | import com.gongbo.excel.export.constants.ExportExcelType; 12 | import com.gongbo.excel.export.core.ExportContextHolder; 13 | import com.gongbo.excel.export.custom.ExportDataConvert; 14 | import com.gongbo.excel.export.custom.FieldFilter; 15 | import com.gongbo.excel.export.custom.FileNameConvert; 16 | import com.gongbo.excel.export.entity.ExportContext; 17 | import com.gongbo.excel.export.entity.ExportFieldInfo; 18 | import com.gongbo.excel.export.entity.ExportFillData; 19 | import io.swagger.annotations.Api; 20 | import org.springframework.validation.annotation.Validated; 21 | import org.springframework.web.bind.annotation.GetMapping; 22 | import org.springframework.web.bind.annotation.RequestMapping; 23 | import org.springframework.web.bind.annotation.RestController; 24 | 25 | import java.time.LocalDate; 26 | import java.time.format.DateTimeFormatter; 27 | import java.util.*; 28 | 29 | @Api(tags = "export") 30 | @RestController 31 | @RequestMapping(value = "/export") 32 | @Validated 33 | public class ExportTestController { 34 | /** 35 | * 导出-简单导出 36 | */ 37 | @GetMapping(value = "test-normal") 38 | @Export 39 | public Result> testNormal() { 40 | return Result.success(ExportDemoView.data()); 41 | } 42 | 43 | /** 44 | * 导出-简单导出(直接返回数组) 45 | */ 46 | @GetMapping(value = "test-normal-array") 47 | @Export 48 | public ExportDemoView[] testNormalData() { 49 | return ExportDemoView.data().toArray(new ExportDemoView[0]); 50 | } 51 | 52 | /** 53 | * 导出-简单导出(直接返回集合) 54 | */ 55 | @GetMapping(value = "test-normal-list") 56 | @Export 57 | public List testNormalList() { 58 | return ExportDemoView.data(); 59 | } 60 | 61 | /** 62 | * 导出-简单导出(直接返回迭代器) 63 | */ 64 | @GetMapping(value = "test-normal-iterable") 65 | @Export 66 | public Iterable testNormalIterable() { 67 | return ExportDemoView.data(); 68 | } 69 | 70 | /** 71 | * 导出-设置导出文件名称 72 | */ 73 | @GetMapping(value = "test-fileName") 74 | @Export(fileName = "文件名称") 75 | public Result> testFilename() { 76 | return Result.success(ExportDemoView.data()); 77 | } 78 | 79 | /** 80 | * 导出-动态设置文件名称 81 | */ 82 | @GetMapping(value = "test-fileName-convert") 83 | @Export(fileNameConvert = CustomFileNameConvert.class) 84 | public Result> testFileNameConvert() { 85 | return Result.success(ExportDemoView.data()); 86 | } 87 | 88 | public static class CustomFileNameConvert implements FileNameConvert { 89 | @Override 90 | public String apply(String fileName) { 91 | return LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); 92 | } 93 | } 94 | 95 | /** 96 | * 97 | */ 98 | @GetMapping(value = "test-fileName-business") 99 | @Export 100 | public Result> testFileNameBusiness() { 101 | if (ExportContextHolder.isExportExcel()) { 102 | ExportContextHolder.getContext().setFileName("动态文件名称"); 103 | } 104 | return Result.success(ExportDemoView.data()); 105 | } 106 | 107 | /** 108 | * 导出-固定Sheet名称 109 | */ 110 | @GetMapping(value = "test-sheetName") 111 | @Export(sheetName = "Sheet0") 112 | public Result> testSheetName() { 113 | return Result.success(ExportDemoView.data()); 114 | } 115 | 116 | /** 117 | * 导出-动态设置Sheet名称 118 | */ 119 | @GetMapping(value = "test-sheetName-business") 120 | @Export 121 | public Result> testSheetNameBusiness() { 122 | if (ExportContextHolder.isExportExcel()) { 123 | ExportContextHolder.getContext().setSheetName("业务中修改Sheet名称"); 124 | } 125 | return Result.success(ExportDemoView.data()); 126 | } 127 | 128 | /** 129 | * 导出到固定文件夹 130 | */ 131 | @GetMapping(value = "test-out-path") 132 | @Export(outputPath = "D:\\WorkDir\\temp\\file") 133 | public Result> testOutPath() { 134 | return Result.success(ExportDemoView.data()); 135 | } 136 | 137 | /** 138 | * 导出-字段过滤 139 | */ 140 | @GetMapping(value = "test-filter") 141 | @Export(fieldFilter = CustomFieldFilter.class) 142 | public Result> testFilter() { 143 | return Result.success(ExportDemoView.data()); 144 | } 145 | 146 | public static class CustomFieldFilter implements FieldFilter { 147 | @Override 148 | public boolean predict(ExportFieldInfo fieldInfo) { 149 | return RandomUtil.randomBoolean(); 150 | } 151 | } 152 | 153 | /** 154 | * 导出-设置导出文件格式 155 | */ 156 | @GetMapping(value = "test-excelType") 157 | @Export(excelType = ExportExcelType.XLS) 158 | public Result> testExcelType() { 159 | return Result.success(ExportDemoView.data()); 160 | } 161 | 162 | /** 163 | * 导出-数据转换 164 | */ 165 | @GetMapping(value = "test-dataConvert") 166 | @Export(dataConvert = CustomExportDataConvert.class) 167 | public Result> testDataConvert() { 168 | return Result.success(ExportDemoView.data()); 169 | } 170 | 171 | public static class CustomExportDataConvert implements ExportDataConvert { 172 | @Override 173 | public List convert(ExportContext exportContext, Object data) { 174 | Result responseEntity = (Result) data; 175 | List list = (List) responseEntity.getData(); 176 | for (int i = 0; i < 20; i++) { 177 | list.add(new ExportDemoView()); 178 | } 179 | return list; 180 | } 181 | } 182 | 183 | /** 184 | * 导出-同一接口多种导出方式 185 | */ 186 | @GetMapping(value = "test-tag") 187 | @Export(tag = "xls", excelType = ExportExcelType.XLS) 188 | @Export(tag = "xlsx", excelType = ExportExcelType.XLSX) 189 | public Result> testTag() { 190 | return Result.success(ExportDemoView.data()); 191 | } 192 | 193 | /** 194 | * 导出-简单模版导出 195 | */ 196 | @GetMapping(value = "test-template-simple") 197 | @Export(template = "template-simply.xlsx") 198 | public Result> testTemplateSimple() { 199 | return Result.success(ExportDemoView.data()); 200 | } 201 | 202 | /** 203 | * 导出-单个Sheet模版导出 204 | */ 205 | @GetMapping(value = "test-template-single-sheet") 206 | @Export(template = "template-single-sheet.xlsx", dataConvert = TemplateSingleSheetDataConvert.class) 207 | public Result> testTemplateSingleSheet() { 208 | return Result.success(ExportDemoView.data()); 209 | } 210 | 211 | public static class TemplateSingleSheetDataConvert implements ExportDataConvert { 212 | @Override 213 | public List convert(ExportContext exportContext, Object data) { 214 | Result responseEntity = (Result) data; 215 | 216 | ExportFillData exportFillData1 = ExportFillData.builder() 217 | .data(responseEntity.getData()) 218 | .build(); 219 | 220 | Map map = new HashMap<>(); 221 | map.put("name", "名称"); 222 | map.put("date", LocalDate.now().format(Times.Formatter.DEFAULT_DATE)); 223 | ExportFillData exportFillData2 = ExportFillData.builder() 224 | .data(map) 225 | .build(); 226 | 227 | return Arrays.asList(exportFillData1, exportFillData2); 228 | } 229 | } 230 | 231 | //导出-模版导出(多个Sheet): 232 | @GetMapping(value = "test-template-much-sheet") 233 | @Export(template = "template-much-sheet.xlsx", dataConvert = TemplateMuchSheetDataConvert.class) 234 | public Result> testTemplateMuchSheet() { 235 | return Result.success(ExportDemoView.data()); 236 | } 237 | 238 | public static class TemplateMuchSheetDataConvert implements ExportDataConvert { 239 | @Override 240 | public List convert(ExportContext exportContext, Object data) { 241 | Result responseEntity = (Result) data; 242 | 243 | Map map = new HashMap<>(); 244 | 245 | map.put("name", "名称"); 246 | map.put("date", LocalDate.now().format(Times.Formatter.DEFAULT_DATE)); 247 | ExportFillData exportFillData2 = ExportFillData.builder() 248 | .sheetName("Sheet1") 249 | .data(map) 250 | .build(); 251 | 252 | ExportFillData exportFillData1 = ExportFillData.builder() 253 | .sheetName("Sheet2") 254 | .data(responseEntity.getData()) 255 | .build(); 256 | 257 | return Arrays.asList(exportFillData1, exportFillData2); 258 | } 259 | } 260 | 261 | /** 262 | * 导出-模版-公式 263 | * 只支持xls格式 264 | */ 265 | @GetMapping(value = "test-template-formula") 266 | @Export(template = "template-formula.xls", dataConvert = TemplateFormulaDataConvert.class) 267 | public Result> testTemplateFormula() { 268 | return Result.success(ExportDemoView.data()); 269 | } 270 | 271 | public static class TemplateFormulaDataConvert implements ExportDataConvert { 272 | @Override 273 | public List convert(ExportContext exportContext, Object data) { 274 | Result responseEntity = (Result) data; 275 | Collection list = (Collection) responseEntity.getData(); 276 | 277 | ExportFillData exportFillData1 = ExportFillData.builder() 278 | .fillConfig(FillConfig.builder().forceNewRow(true).build()) 279 | .data(new FillWrapper("data", list)) 280 | .build(); 281 | 282 | int start = 1; 283 | int end = start + (CollUtil.isEmpty(list) ? 0 : list.size() - 1); 284 | 285 | Map constantMap2 = new HashMap<>(); 286 | constantMap2.put("data_end", end); 287 | 288 | ExportFillData exportFillData2 = ExportFillData.builder() 289 | .data(constantMap2) 290 | .build(); 291 | 292 | return Arrays.asList(exportFillData1, exportFillData2); 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/controller/ImportControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example.controller; 2 | 3 | import com.gongbo.excel.example.result.Result; 4 | import com.gongbo.excel.example.view.ExportDemoView; 5 | import com.gongbo.excel.imports.annotations.Import; 6 | import io.swagger.annotations.Api; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | /** 11 | *

12 | * 电量指标 前端控制器 13 | *

14 | * 15 | * @author gongbo 16 | * @since 2021-01-04 17 | */ 18 | @Api(tags = "import") 19 | @RestController 20 | @RequestMapping(value = "/import") 21 | @Validated 22 | public class ImportControllerTest { 23 | 24 | /** 25 | * 导入-数据导入 26 | * RequestBody注解required属性 必须设置为false 27 | * 参数可以是数组、集合 28 | */ 29 | @PostMapping(value = "test-import") 30 | @Import 31 | public Result testImport(@RequestBody(required = false) ExportDemoView[] param) { 32 | return Result.success(param); 33 | } 34 | 35 | /** 36 | * 导入-模版下载 37 | */ 38 | @GetMapping(value = "test-template") 39 | @Import(model = ExportDemoView.class) 40 | public void testTemplate() { 41 | } 42 | 43 | /** 44 | * 导入-自定义模版下载 45 | */ 46 | @GetMapping(value = "test-custom-template") 47 | @Import(model = ExportDemoView.class, template = "template-import.xlsx", templateFilename = "自定义模板") 48 | public void testCustomTemplate() { 49 | } 50 | 51 | /** 52 | * 导入-数据导入、模版下载 53 | * RequestBody注解required属性 必须设置为false 54 | * 必须支持GET、POST请求 55 | * 参数可以是数组、集合 56 | */ 57 | @RequestMapping(value = "test-import-template", method = {RequestMethod.GET, RequestMethod.POST}) 58 | @Import 59 | public Result testImportTemplate(@RequestBody(required = false) ExportDemoView[] param) { 60 | return Result.success(param); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/result/Result.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example.result; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class Result { 9 | 10 | private Integer code; 11 | 12 | private String message; 13 | 14 | private T data; 15 | 16 | public Result(Integer code, String message) { 17 | this.code = code; 18 | this.message = message; 19 | } 20 | 21 | public Result(Integer code, String message, T data) { 22 | this.code = code; 23 | this.message = message; 24 | this.data = data; 25 | } 26 | 27 | public Result(ResultCode resultCode, T data) { 28 | this.code = resultCode.getCode(); 29 | this.message = resultCode.getMessage(); 30 | this.data = data; 31 | } 32 | 33 | public static Result success(T data) { 34 | return new Result(ResultCode.SUCCESS, data); 35 | } 36 | 37 | public static Result success() { 38 | return success(null); 39 | } 40 | 41 | public static Result fail(T data) { 42 | return new Result(ResultCode.FAIL, data); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/result/ResultCode.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example.result; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | @RequiredArgsConstructor 7 | @Getter 8 | public enum ResultCode { 9 | SUCCESS(0, "success"), 10 | FAIL(100, "fail"), 11 | ERROR(500, "error"); 12 | 13 | private final int code; 14 | private final String message; 15 | } 16 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/java/com/gongbo/excel/example/view/ExportDemoView.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.example.view; 2 | 3 | import cn.hutool.core.util.RandomUtil; 4 | import com.alibaba.excel.annotation.ExcelProperty; 5 | import com.alibaba.excel.annotation.write.style.ColumnWidth; 6 | import com.alibaba.excel.annotation.write.style.ContentRowHeight; 7 | import com.gongbo.excel.adapter.easyexcel.converter.DefaultEnumConvert; 8 | import com.gongbo.excel.example.constants.GenderEnum; 9 | import lombok.Data; 10 | 11 | import java.math.BigDecimal; 12 | import java.time.LocalDate; 13 | import java.time.LocalDateTime; 14 | import java.util.Date; 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.Stream; 18 | 19 | @Data 20 | @ColumnWidth(12) 21 | @ContentRowHeight(18) 22 | public class ExportDemoView { 23 | 24 | @ExcelProperty("文本") 25 | private String text = RandomUtil.randomString(8); 26 | 27 | @ExcelProperty("整数") 28 | private Integer integerValue = RandomUtil.randomInt(10000); 29 | 30 | @ExcelProperty("浮点数") 31 | private Float floatValue = (float) RandomUtil.randomDouble(-10000, 10000); 32 | 33 | @ExcelProperty("长浮点数") 34 | private Double doubleValue = RandomUtil.randomDouble(-10000, 10000); 35 | 36 | @ExcelProperty("定点数") 37 | private BigDecimal bigDecimal = RandomUtil.randomBigDecimal(BigDecimal.valueOf(10_000)); 38 | 39 | @ExcelProperty("日期") 40 | private LocalDate localDate = LocalDate.now(); 41 | 42 | @ExcelProperty("日期时间") 43 | @ColumnWidth(20) 44 | private LocalDateTime localDateTime = LocalDateTime.now(); 45 | 46 | @ExcelProperty("时间") 47 | @ColumnWidth(20) 48 | private Date date = new Date(); 49 | 50 | @ExcelProperty(value = "性别", converter = DefaultEnumConvert.class) 51 | private GenderEnum gender = GenderEnum.valueOf(RandomUtil.randomInt(3)); 52 | 53 | public static List data() { 54 | return Stream.generate(ExportDemoView::new) 55 | .limit(RandomUtil.randomInt(1, 20)) 56 | .collect(Collectors.toList()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: export_demo 4 | mvc: 5 | pathmatch: 6 | matching-strategy: ANT_PATH_MATCHER 7 | 8 | server: 9 | port: 80 10 | 11 | easyexcel-plus: 12 | export: 13 | #默认导出Sheet名称 14 | default-sheet-name: Sheet1 15 | #模板文件路径 16 | template-dir: classpath:templates 17 | #默认导出文件格式 18 | default-excel-type: xlsx 19 | #默认导出方式 20 | default-export-by: easy_excel 21 | import: 22 | #默认导入读取的Sheet名称 23 | default-sheet-name: Sheet1 24 | #模板文件路径 25 | template-dir: classpath:templates 26 | #默认导入方式 27 | default-import-by: easy_excel 28 | #读取excel超时时间,不设置或设置为0时无读取时间限制 29 | read-timeout: 60000 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/resources/templates/template-formula.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongbox/easyexcel-plus/06a1b93a9da024d077610d771692d5496ceaad06/easyexcel-plus-boot-starter-test/src/main/resources/templates/template-formula.xls -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/resources/templates/template-import.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongbox/easyexcel-plus/06a1b93a9da024d077610d771692d5496ceaad06/easyexcel-plus-boot-starter-test/src/main/resources/templates/template-import.xlsx -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/resources/templates/template-much-sheet.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongbox/easyexcel-plus/06a1b93a9da024d077610d771692d5496ceaad06/easyexcel-plus-boot-starter-test/src/main/resources/templates/template-much-sheet.xlsx -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/resources/templates/template-simply.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongbox/easyexcel-plus/06a1b93a9da024d077610d771692d5496ceaad06/easyexcel-plus-boot-starter-test/src/main/resources/templates/template-simply.xlsx -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter-test/src/main/resources/templates/template-single-sheet.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongbox/easyexcel-plus/06a1b93a9da024d077610d771692d5496ceaad06/easyexcel-plus-boot-starter-test/src/main/resources/templates/template-single-sheet.xlsx -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | easyexcel-plus-parent 7 | io.github.gongbox 8 | 2.4.1 9 | 10 | 4.0.0 11 | 12 | easyexcel-plus-boot-starter 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | io.github.gongbox 22 | easyexcel-plus 23 | ${project.parent.version} 24 | compile 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-autoconfigure 29 | compile 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-configuration-processor 34 | true 35 | 36 | 37 | 38 | 39 | 40 | 41 | src/main/resources 42 | 43 | **/* 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter/src/main/java/com/gongbo/excel/EasyExcelPlusConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel; 2 | 3 | import com.gongbo.excel.export.config.ExportConfig; 4 | import com.gongbo.excel.export.config.ExportProperties; 5 | import com.gongbo.excel.imports.config.ImportConfig; 6 | import com.gongbo.excel.imports.config.ImportProperties; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 8 | import org.springframework.boot.context.properties.ConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Import; 12 | 13 | @Configuration 14 | @Import({ImportConfig.class, ExportConfig.class}) 15 | public class EasyExcelPlusConfiguration { 16 | 17 | @Bean 18 | @ConfigurationProperties("easyexcel-plus.export") 19 | @ConditionalOnMissingBean 20 | public ExportProperties exportProperties() { 21 | return new ExportProperties(); 22 | } 23 | 24 | @Bean 25 | @ConfigurationProperties("easyexcel-plus.import") 26 | @ConditionalOnMissingBean 27 | public ImportProperties importProperties() { 28 | return new ImportProperties(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /easyexcel-plus-boot-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gongbo.excel.EasyExcelPlusConfiguration -------------------------------------------------------------------------------- /easyexcel-plus-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | easyexcel-plus-parent 7 | io.github.gongbox 8 | 2.4.1 9 | 10 | 4.0.0 11 | 12 | easyexcel-plus-common 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | javax.servlet 22 | javax.servlet-api 23 | compile 24 | 25 | 26 | org.springframework 27 | spring-web 28 | provided 29 | 30 | 31 | org.slf4j 32 | slf4j-api 33 | compile 34 | 35 | 36 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/adapter/Adapter.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.adapter; 2 | 3 | public interface Adapter { 4 | /** 5 | * 名称 6 | * 7 | * @return 8 | */ 9 | String name(); 10 | } 11 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/enums/ExcelType.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.enums; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | @Getter 7 | @RequiredArgsConstructor 8 | public enum ExcelType { 9 | XLS(".xls"), 10 | XLSX(".xlsx"); 11 | 12 | private final String value; 13 | } -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/utils/BeanMap.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.utils; 2 | 3 | import org.springframework.beans.BeanUtils; 4 | 5 | import java.util.Collections; 6 | import java.util.List; 7 | import java.util.function.Supplier; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * bean转换工具类 12 | */ 13 | public interface BeanMap { 14 | 15 | /** 16 | * 转换为其他类型对象 17 | * 18 | * @param clazz 19 | * @param 20 | * @return 21 | */ 22 | default T mapTo(Class clazz) { 23 | return mapTo(() -> BeanUtils.instantiateClass(clazz)); 24 | } 25 | 26 | /** 27 | * 转换为其他类型对象 28 | * 29 | * @param supplier 30 | * @param 31 | * @return 32 | */ 33 | default T mapTo(Supplier supplier) { 34 | return mapTo(this, supplier); 35 | } 36 | 37 | /** 38 | * 转换为其他类型对象 39 | * 40 | * @param clazz 41 | * @param 42 | * @return 43 | */ 44 | static T mapTo(Object obj, Class clazz) { 45 | return mapTo(obj, () -> BeanUtils.instantiateClass(clazz)); 46 | } 47 | 48 | /** 49 | * 转换为其他类型对象 50 | * 51 | * @param supplier 52 | * @param 53 | * @return 54 | */ 55 | static T mapTo(Object obj, Supplier supplier) { 56 | if (obj == null) return null; 57 | T data = supplier.get(); 58 | BeanUtils.copyProperties(obj, data); 59 | return data; 60 | } 61 | 62 | static T mapTo(Object obj, Supplier supplier, Class clazz) { 63 | if (obj == null) return null; 64 | if (supplier != null) { 65 | return mapTo(obj, supplier); 66 | } 67 | return mapTo(obj, clazz); 68 | } 69 | 70 | /** 71 | * 转换为其他类型对象 72 | * 73 | * @param clazz 74 | * @param 75 | * @return 76 | */ 77 | static List mapListTo(List list, Class clazz) { 78 | if (list == null) return null; 79 | if (list.isEmpty()) return Collections.emptyList(); 80 | return list.stream().map(o -> mapTo(o, clazz)).collect(Collectors.toList()); 81 | } 82 | 83 | /** 84 | * 转换为其他类型对象 85 | * 86 | * @param supplier 87 | * @param 88 | * @return 89 | */ 90 | static List mapListTo(List list, Supplier supplier) { 91 | if (list == null) return null; 92 | if (list.isEmpty()) return Collections.emptyList(); 93 | return list.stream().map(o -> mapTo(o, supplier)).collect(Collectors.toList()); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/utils/CollectionUtil.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.util.Collection; 7 | import java.util.Map; 8 | 9 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 10 | public class CollectionUtil { 11 | 12 | /** 13 | * @param collection 14 | * @return 15 | */ 16 | public static boolean isNotEmpty(Collection collection) { 17 | return !isEmpty(collection); 18 | } 19 | 20 | /** 21 | * @param collection 22 | * @return 23 | */ 24 | public static boolean isEmpty(Collection collection) { 25 | return collection == null || collection.isEmpty(); 26 | } 27 | 28 | /** 29 | * @param map 30 | * @return 31 | */ 32 | public static boolean isEmpty(Map map) { 33 | return map == null || map.isEmpty(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/utils/ReflectUtil.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.lang.reflect.Field; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public class ReflectUtil { 13 | 14 | /** 15 | * @param beanClass 16 | * @param withSuperClassFields 17 | * @return 18 | * @throws SecurityException 19 | */ 20 | public static List getFields(Class beanClass, boolean withSuperClassFields) throws SecurityException { 21 | List fields = new ArrayList<>(); 22 | 23 | for (Class searchType = beanClass; searchType != null; searchType = withSuperClassFields ? searchType.getSuperclass() : null) { 24 | Field[] declaredFields = searchType.getDeclaredFields(); 25 | fields.addAll(Arrays.asList(declaredFields)); 26 | } 27 | return fields; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/utils/ResponseUtils.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | import javax.servlet.http.HttpServletResponse; 7 | import java.io.UnsupportedEncodingException; 8 | import java.net.URLEncoder; 9 | import java.nio.charset.StandardCharsets; 10 | 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public class ResponseUtils { 13 | 14 | /** 15 | * 设置下载文件名 16 | * 17 | * @param fileName 18 | * @param response 19 | */ 20 | public static void setDownloadFileHeader(HttpServletResponse response, String fileName) { 21 | // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman 22 | response.addHeader("Content-Type", "application/vnd.ms-excel;charset=UTF-8"); 23 | try { 24 | // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 25 | fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replaceAll("\\+", "%20"); 26 | } catch (UnsupportedEncodingException ignored) { 27 | } 28 | 29 | response.addHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/utils/StringPool.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.utils; 2 | 3 | public interface StringPool { 4 | String AMPERSAND = "&"; 5 | String AND = "and"; 6 | String AT = "@"; 7 | String ASTERISK = "*"; 8 | String STAR = ASTERISK; 9 | String BACK_SLASH = "\\"; 10 | String COLON = ":"; 11 | String COMMA = ","; 12 | String DASH = "-"; 13 | String DOLLAR = "$"; 14 | String DOT = "."; 15 | String DOTDOT = ".."; 16 | String DOT_CLASS = ".class"; 17 | String DOT_JAVA = ".java"; 18 | String DOT_XML = ".xml"; 19 | String EMPTY = ""; 20 | String EQUALS = "="; 21 | String FALSE = "false"; 22 | String SLASH = "/"; 23 | String HASH = "#"; 24 | String HAT = "^"; 25 | String LEFT_BRACE = "{"; 26 | String LEFT_BRACKET = "("; 27 | String LEFT_CHEV = "<"; 28 | String DOT_NEWLINE = ",\n"; 29 | String NEWLINE = "\n"; 30 | String N = "n"; 31 | String NO = "no"; 32 | String NULL = "null"; 33 | String OFF = "off"; 34 | String ON = "on"; 35 | String PERCENT = "%"; 36 | String PIPE = "|"; 37 | String PLUS = "+"; 38 | String QUESTION_MARK = "?"; 39 | String EXCLAMATION_MARK = "!"; 40 | String QUOTE = "\""; 41 | String RETURN = "\r"; 42 | String TAB = "\t"; 43 | String RIGHT_BRACE = "}"; 44 | String RIGHT_BRACKET = ")"; 45 | String RIGHT_CHEV = ">"; 46 | String SEMICOLON = ";"; 47 | String SINGLE_QUOTE = "'"; 48 | String BACKTICK = "`"; 49 | String SPACE = " "; 50 | String TILDA = "~"; 51 | String LEFT_SQ_BRACKET = "["; 52 | String RIGHT_SQ_BRACKET = "]"; 53 | String TRUE = "true"; 54 | String UNDERSCORE = "_"; 55 | String UTF_8 = "UTF-8"; 56 | String US_ASCII = "US-ASCII"; 57 | String ISO_8859_1 = "ISO-8859-1"; 58 | String Y = "y"; 59 | String YES = "yes"; 60 | String ONE = "1"; 61 | String ZERO = "0"; 62 | String DOLLAR_LEFT_BRACE = "${"; 63 | String HASH_LEFT_BRACE = "#{"; 64 | String CRLF = "\r\n"; 65 | 66 | String HTML_NBSP = " "; 67 | String HTML_AMP = "&"; 68 | String HTML_QUOTE = """; 69 | String HTML_LT = "<"; 70 | String HTML_GT = ">"; 71 | } 72 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/utils/StringUtil.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.utils; 2 | 3 | public class StringUtil { 4 | /** 5 | * @param text 6 | * @return 7 | */ 8 | public static boolean isEmpty(String text) { 9 | return text == null || text.isEmpty(); 10 | } 11 | 12 | /** 13 | * @param text 14 | * @return 15 | */ 16 | public static boolean isNotEmpty(String text) { 17 | return !isEmpty(text); 18 | } 19 | 20 | /** 21 | * 获取第一个非空的字符串,如果所有对象都为空,则返回null 22 | */ 23 | public static String firstNotEmpty(String... values) { 24 | for (String value : values) { 25 | if (isNotEmpty(value)) { 26 | return value; 27 | } 28 | } 29 | return null; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/utils/TemplateUtils.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.utils; 2 | 3 | import com.gongbo.excel.common.enums.ExcelType; 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | import org.springframework.core.io.ClassPathResource; 7 | import org.springframework.core.io.FileUrlResource; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.nio.file.Files; 13 | import java.nio.file.Paths; 14 | import java.util.Objects; 15 | 16 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 17 | public class TemplateUtils { 18 | 19 | public static final String CLASSPATH_PATH_PREFIX = "classpath:"; 20 | public static final String FILE_PATH_PREFIX = "file:"; 21 | 22 | public static InputStream getTemplateInputStream(String templateDir, String templateFileName) throws IOException { 23 | String templatePath; 24 | if (templateFileName.startsWith(CLASSPATH_PATH_PREFIX) || templateFileName.startsWith(FILE_PATH_PREFIX)) { 25 | templatePath = templateFileName; 26 | } else { 27 | //没有指定具体路径时必须配置templateDir 28 | Objects.requireNonNull(templateDir); 29 | if (templateDir.endsWith(File.separator) || templateFileName.startsWith(File.separator)) { 30 | templatePath = templateDir + templateFileName; 31 | } else { 32 | templatePath = templateDir + File.separator + templateFileName; 33 | } 34 | } 35 | 36 | InputStream inputStream; 37 | if (templatePath.startsWith(CLASSPATH_PATH_PREFIX)) { 38 | ClassPathResource resource = new ClassPathResource(templatePath.replaceFirst(CLASSPATH_PATH_PREFIX, "")); 39 | inputStream = resource.getInputStream(); 40 | } else if (templatePath.startsWith(FILE_PATH_PREFIX)) { 41 | FileUrlResource fileUrlResource = new FileUrlResource(templatePath.replaceFirst(FILE_PATH_PREFIX, "")); 42 | inputStream = fileUrlResource.getInputStream(); 43 | } else { 44 | inputStream = Files.newInputStream(Paths.get(templatePath)); 45 | } 46 | return inputStream; 47 | } 48 | 49 | /** 50 | * 获取模板格式 51 | * 52 | * @param template 53 | * @return 54 | */ 55 | public static ExcelType getTemplateExcelType(String template) { 56 | int i = template.lastIndexOf("."); 57 | String type = template.substring(i); 58 | for (ExcelType value : ExcelType.values()) { 59 | if (value.getValue().equalsIgnoreCase(type)) { 60 | return value; 61 | } 62 | } 63 | throw new IllegalArgumentException(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/utils/Times.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.time.format.DateTimeFormatter; 7 | 8 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 9 | public class Times { 10 | 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public static class Pattern { 13 | public static final String DEFAULT_YEAR_MONTH = "yyyy-MM"; 14 | public static final String DEFAULT_DATE = "yyyy-MM-dd"; 15 | public static final String DEFAULT_DATE_TIME_NO_SECOND = "yyyy-MM-dd HH:mm"; 16 | public static final String DEFAULT_DATE_TIME = "yyyy-MM-dd HH:mm:ss"; 17 | public static final String DEFAULT_TIME_NO_SECOND = "HH:mm"; 18 | public static final String DEFAULT_TIME = "HH:mm:ss"; 19 | public static final String DEFAULT_YEAR = "yyyy"; 20 | } 21 | 22 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 23 | public static class Formatter { 24 | public static final DateTimeFormatter DEFAULT_YEAR_MONTH = DateTimeFormatter.ofPattern(Pattern.DEFAULT_YEAR_MONTH); 25 | public static final DateTimeFormatter DEFAULT_DATE = DateTimeFormatter.ofPattern(Pattern.DEFAULT_DATE); 26 | public static final DateTimeFormatter DEFAULT_DATE_TIME_NO_SECOND = DateTimeFormatter.ofPattern(Pattern.DEFAULT_DATE_TIME_NO_SECOND); 27 | public static final DateTimeFormatter DEFAULT_DATE_TIME = DateTimeFormatter.ofPattern(Pattern.DEFAULT_DATE_TIME); 28 | public static final DateTimeFormatter DEFAULT_TIME_NO_SECOND = DateTimeFormatter.ofPattern(Pattern.DEFAULT_TIME_NO_SECOND); 29 | public static final DateTimeFormatter DEFAULT_TIME = DateTimeFormatter.ofPattern(Pattern.DEFAULT_TIME); 30 | public static final DateTimeFormatter DEFAULT_YEAR = DateTimeFormatter.ofPattern(Pattern.DEFAULT_YEAR); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.util.Collection; 7 | import java.util.Collections; 8 | import java.util.function.Supplier; 9 | 10 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 11 | public class Utils { 12 | /** 13 | * 获取第一个非空的对象,如果所有对象都为空,则返回空 14 | * 15 | * @param values 16 | * @param 17 | * @return 18 | */ 19 | public static T firstNotNull(T... values) { 20 | for (T value : values) { 21 | if (value != null) { 22 | return value; 23 | } 24 | } 25 | return null; 26 | } 27 | 28 | /** 29 | * 获取第一个非空的对象,如果所有对象都为空,则返回空 30 | * 31 | * @param values 32 | * @param 33 | * @return 34 | */ 35 | @SafeVarargs 36 | public static T firstNotNull(Supplier... values) { 37 | for (Supplier supplier : values) { 38 | T value = supplier.get(); 39 | if (value != null) { 40 | return value; 41 | } 42 | } 43 | return null; 44 | } 45 | 46 | /** 47 | * 获取第一个非空的字符串,如果所有对象都为空,则返回null 48 | * 49 | * @param values 50 | * @return 51 | */ 52 | public static String firstNotEmpty(String... values) { 53 | for (String value : values) { 54 | if (StringUtil.isNotEmpty(value)) { 55 | return value; 56 | } 57 | } 58 | return null; 59 | } 60 | 61 | /** 62 | * 获取第一个非空的字符串,如果所有对象都为空,则返回null 63 | * 64 | * @param values 65 | * @return 66 | */ 67 | @SafeVarargs 68 | public static String firstNotEmpty(Supplier... values) { 69 | for (Supplier supplier : values) { 70 | String value = supplier.get(); 71 | if (StringUtil.isNotEmpty(value)) { 72 | return value; 73 | } 74 | } 75 | return null; 76 | } 77 | 78 | /** 79 | * 获取第一个非空的字符串,如果所有对象都为空,则返回空集合 80 | * 81 | * @param values 82 | * @param 83 | * @return 84 | */ 85 | public static Collection firstNotEmpty(Collection... values) { 86 | for (Collection value : values) { 87 | if (CollectionUtil.isNotEmpty(value)) { 88 | return value; 89 | } 90 | } 91 | return Collections.emptyList(); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /easyexcel-plus-common/src/main/java/com/gongbo/excel/common/utils/WebUtils.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.common.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | import org.springframework.web.context.request.RequestContextHolder; 6 | import org.springframework.web.context.request.ServletRequestAttributes; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.util.Optional; 11 | 12 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 13 | public class WebUtils { 14 | 15 | /** 16 | * 获取HttpServletResponse 17 | * 18 | * @return HttpServletResponse 19 | */ 20 | public static HttpServletResponse getCurrentResponse() { 21 | return Optional.ofNullable((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) 22 | .map(ServletRequestAttributes::getResponse) 23 | .orElse(null); 24 | } 25 | 26 | /** 27 | * 获取HttpServletRequest 28 | * 29 | * @return HttpServletRequest 30 | */ 31 | public static HttpServletRequest getCurrentRequest() { 32 | return Optional.ofNullable((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) 33 | .map(ServletRequestAttributes::getRequest) 34 | .orElse(null); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /easyexcel-plus-export/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | easyexcel-plus-parent 7 | io.github.gongbox 8 | 2.4.1 9 | 10 | 4.0.0 11 | 12 | easyexcel-plus-export 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | io.github.gongbox 22 | easyexcel-plus-common 23 | ${project.parent.version} 24 | provided 25 | 26 | 27 | org.springframework 28 | spring-context-support 29 | provided 30 | 31 | 32 | org.springframework 33 | spring-aop 34 | provided 35 | 36 | 37 | org.aspectj 38 | aspectjweaver 39 | provided 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-autoconfigure 44 | provided 45 | 46 | 47 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/adapter/ExportAdapter.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.adapter; 2 | 3 | import com.gongbo.excel.common.adapter.Adapter; 4 | import com.gongbo.excel.export.entity.ExportContext; 5 | import com.gongbo.excel.export.entity.ExportFieldInfo; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | import java.util.List; 11 | 12 | public interface ExportAdapter extends Adapter { 13 | 14 | /** 15 | * 普通导出 16 | * 17 | * @param exportContext 18 | * @param data 19 | * @param outputStream 20 | * @throws IOException 21 | */ 22 | void export(ExportContext exportContext, List data, OutputStream outputStream) throws IOException; 23 | 24 | /** 25 | * 模板导出 26 | * 27 | * @param exportContext 28 | * @param templateInputStream 29 | * @param data 30 | * @param outputStream 31 | */ 32 | void export(ExportContext exportContext, InputStream templateInputStream, List data, OutputStream outputStream); 33 | 34 | /** 35 | * 查找导出的字段信息列表 36 | * 37 | * @param clazz 38 | * @return 39 | */ 40 | List findExportFieldInfos(Class clazz); 41 | } 42 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/adapter/ExportAdapters.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.adapter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.util.Collection; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | 10 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 11 | public class ExportAdapters { 12 | private static final List ADAPTERS = new LinkedList<>(); 13 | 14 | /** 15 | * 添加适配器 16 | * 17 | * @param adapter 18 | */ 19 | public static void addAdapter(ExportAdapter adapter) { 20 | ADAPTERS.add(adapter); 21 | } 22 | 23 | /** 24 | * 获取所有适配器 25 | * 26 | * @return 27 | */ 28 | public static Collection getAdapters() { 29 | return ADAPTERS; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/advise/ExportAdvise.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.advise; 2 | 3 | 4 | import com.gongbo.excel.export.config.ExportProperties; 5 | import com.gongbo.excel.export.core.lifecycle.DefaultExportLifecycle; 6 | import com.gongbo.excel.export.core.ExportProxy; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.aspectj.lang.ProceedingJoinPoint; 9 | import org.aspectj.lang.annotation.Around; 10 | import org.aspectj.lang.annotation.Aspect; 11 | import org.aspectj.lang.annotation.Pointcut; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | 14 | @Aspect 15 | @Slf4j 16 | public class ExportAdvise { 17 | 18 | @Autowired 19 | private ExportProperties exportProperties; 20 | 21 | @Pointcut("@annotation(com.gongbo.excel.export.annotations.Export) || @annotation(com.gongbo.excel.export.annotations.Exports)") 22 | public void doExport() { 23 | } 24 | 25 | /** 26 | * @param joinPoint 27 | * @return 28 | * @throws Throwable 29 | */ 30 | @Around("doExport()") 31 | public Object around(ProceedingJoinPoint joinPoint) throws Throwable { 32 | ExportProxy exportProxy = new ExportProxy(exportProperties, new DefaultExportLifecycle()); 33 | //判断是否是导出 34 | if (exportProxy.isExport()) { 35 | try { 36 | //执行导出 37 | return exportProxy.export(joinPoint); 38 | } catch (Throwable e) { 39 | log.error("export error", e); 40 | throw e; 41 | } 42 | } 43 | return joinPoint.proceed(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/annotations/Export.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.annotations; 2 | 3 | 4 | import com.gongbo.excel.export.constants.ExportExcelType; 5 | import com.gongbo.excel.export.custom.ExportDataConvert; 6 | import com.gongbo.excel.export.custom.FieldFilter; 7 | import com.gongbo.excel.export.custom.FileNameConvert; 8 | import com.gongbo.excel.export.custom.defaults.DefaultExportDataConvert; 9 | import com.gongbo.excel.export.custom.defaults.DefaultFieldFilter; 10 | import com.gongbo.excel.export.custom.defaults.DefaultFileNameConvert; 11 | 12 | import java.lang.annotation.*; 13 | 14 | @Repeatable(Exports.class) 15 | @Target({ElementType.METHOD}) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Documented 18 | public @interface Export { 19 | 20 | /** 21 | * 导出文件名,否则为时间戳 22 | */ 23 | String fileName() default ""; 24 | 25 | /** 26 | * 导出sheet名称,为空时取默认值 27 | */ 28 | String sheetName() default ""; 29 | 30 | /** 31 | * 导出模板文件名称 32 | */ 33 | String template() default ""; 34 | 35 | /** 36 | * 导出分组,同一个接口支持多种导出时,可以根据前端请求参数中的export_tag匹配对应的导出注解配置 37 | */ 38 | String tag() default ""; 39 | 40 | /** 41 | * 导出模型类 42 | * 值为AutoModel.class(默认)时代表根据方法返回类型查找(要求该方法返回类型为ResponseEntity> / 或 ResponseEntity) 43 | * 值为Export.NoneModel.class时代表没有模型类,注意这时导出方式不能为AUTO 44 | * 值为其他时代表指定为该模型类 45 | */ 46 | Class modelClass() default AutoModel.class; 47 | 48 | /** 49 | * 输出文件地址.这时excel将输出到指定目录,且不会通过http响应 50 | * 格式:固定地址,如:D:\WorkDir\temp\ 51 | * 格式:变量形式,如:${java.io.tmpdir} 52 | */ 53 | String outputPath() default ""; 54 | 55 | /** 56 | * 动态名称,优先级大于fileName 57 | */ 58 | Class fileNameConvert() default DefaultFileNameConvert.class; 59 | 60 | /** 61 | * 字段过滤 62 | */ 63 | Class fieldFilter() default DefaultFieldFilter.class; 64 | 65 | /** 66 | * 数据转换 67 | */ 68 | Class dataConvert() default DefaultExportDataConvert.class; 69 | 70 | /** 71 | * 导出excel文件格式 72 | */ 73 | ExportExcelType excelType() default ExportExcelType.AUTO; 74 | 75 | /** 76 | * 指定导出方式,否则为默认值 77 | */ 78 | String exportBy() default ""; 79 | 80 | /** 81 | * 是否执行公式 82 | */ 83 | boolean formula() default true; 84 | 85 | /** 86 | * 是否响应请求结果 87 | */ 88 | boolean responseResult() default false; 89 | 90 | /** 91 | * 没有模型类 92 | */ 93 | final class NoneModel { 94 | } 95 | 96 | /** 97 | * 自动获取模型类 98 | * 1,模板导出时,模型类为NoneModel 99 | * 2,非模板导出时,模型类根据方法返回获取 100 | */ 101 | final class AutoModel { 102 | } 103 | } -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/annotations/Exports.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.annotations; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 多重注解支持 7 | */ 8 | @Target({ElementType.METHOD}) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Documented 11 | public @interface Exports { 12 | Export[] value(); 13 | } 14 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/config/ExportConfig.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.config; 2 | 3 | import com.gongbo.excel.export.advise.ExportAdvise; 4 | import com.gongbo.excel.export.core.resulthandler.DefaultResultHandler; 5 | import org.springframework.context.annotation.Import; 6 | 7 | @Import({ExportAdvise.class, DefaultResultHandler.class}) 8 | public class ExportConfig { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/config/ExportProperties.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.config; 2 | 3 | import com.gongbo.excel.export.core.resulthandler.ResultHandler; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import lombok.ToString; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | 9 | @Getter 10 | @Setter 11 | @ToString 12 | public class ExportProperties { 13 | /** 14 | * 默认Sheet名称 15 | */ 16 | private String defaultSheetName = "Sheet1"; 17 | 18 | /** 19 | * 填充key名 20 | */ 21 | private String fillKey = "fill_data"; 22 | 23 | /** 24 | * 模板文件路径 25 | */ 26 | private String templateDir = ""; 27 | 28 | /** 29 | * 公式开始前缀 30 | */ 31 | private String formulaPrefix = "#formula"; 32 | 33 | /** 34 | * 默认导出excel格式 35 | */ 36 | private String defaultExcelType = "xlsx"; 37 | 38 | /** 39 | * 默认导出方式 40 | */ 41 | private String defaultExportBy = "easy_excel"; 42 | 43 | @Autowired 44 | private ResultHandler resultHandler; 45 | } 46 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/constants/ExportExcelType.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.constants; 2 | 3 | import com.gongbo.excel.common.enums.ExcelType; 4 | import lombok.Getter; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Getter 8 | @RequiredArgsConstructor 9 | public enum ExportExcelType { 10 | /** 11 | * 自动, 12 | * 1:模板导出时,根据模板格式确定 13 | * 2:非模板导出时,取默认配置导出 14 | */ 15 | AUTO(null), 16 | XLS(ExcelType.XLS), 17 | XLSX(ExcelType.XLSX); 18 | 19 | private final ExcelType excelType; 20 | } -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/core/ExportContextHolder.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.core; 2 | 3 | 4 | import com.gongbo.excel.export.entity.ExportContext; 5 | import com.gongbo.excel.export.param.ExportParam; 6 | import lombok.AccessLevel; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.Optional; 10 | 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public class ExportContextHolder { 13 | 14 | private static final ThreadLocal THREAD_LOCAL = new ThreadLocal<>(); 15 | 16 | /** 17 | * 当前接口是否正在执行导出 18 | * 19 | * @return 20 | */ 21 | public static boolean isExportExcel() { 22 | return Optional.ofNullable(getContext()) 23 | .map(ExportContext::getExportParam) 24 | .map(ExportParam::isExcel) 25 | .orElse(false); 26 | } 27 | 28 | /** 29 | * 获取导出上下文 30 | * 31 | * @return 32 | */ 33 | public static ExportContext getContext() { 34 | return THREAD_LOCAL.get(); 35 | } 36 | 37 | /** 38 | * 设置导出上下文 39 | * 40 | * @param exportContext 41 | */ 42 | public static void setContext(ExportContext exportContext) { 43 | THREAD_LOCAL.set(exportContext); 44 | } 45 | 46 | /** 47 | * 清空 48 | */ 49 | public static void clear() { 50 | THREAD_LOCAL.remove(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/core/ExportHandlers.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.core; 2 | 3 | 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | import org.springframework.beans.BeanUtils; 7 | 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public class ExportHandlers { 13 | /** 14 | * Cache 15 | */ 16 | private static final Map, Object> handlerCache = new ConcurrentHashMap<>(); 17 | 18 | /** 19 | * @param clazz 20 | * @param 21 | * @return 22 | */ 23 | public static T of(Class clazz) { 24 | return (T) handlerCache.computeIfAbsent(clazz, BeanUtils::instantiateClass); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/core/ExportProxy.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.core; 2 | 3 | 4 | import com.gongbo.excel.common.utils.WebUtils; 5 | import com.gongbo.excel.export.adapter.ExportAdapter; 6 | import com.gongbo.excel.export.adapter.ExportAdapters; 7 | import com.gongbo.excel.export.config.ExportProperties; 8 | import com.gongbo.excel.export.core.lifecycle.ExportLifecycle; 9 | import com.gongbo.excel.export.entity.ExportContext; 10 | import com.gongbo.excel.export.param.ExportParam; 11 | import lombok.RequiredArgsConstructor; 12 | import org.aspectj.lang.ProceedingJoinPoint; 13 | import org.aspectj.lang.reflect.MethodSignature; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import java.io.OutputStream; 17 | import java.lang.reflect.Method; 18 | import java.util.Collection; 19 | import java.util.List; 20 | import java.util.Objects; 21 | 22 | @RequiredArgsConstructor 23 | public class ExportProxy { 24 | 25 | private final ExportProperties exportProperties; 26 | private final ExportLifecycle exportLifecycle; 27 | 28 | /** 29 | * 检测是否导出 30 | */ 31 | public boolean isExport() { 32 | return exportLifecycle.isExportRequest(exportProperties, WebUtils.getCurrentRequest()); 33 | } 34 | 35 | /** 36 | * 执行导出 37 | * 38 | * @param joinPoint 39 | * @return 40 | * @throws Throwable 41 | */ 42 | public Object export(ProceedingJoinPoint joinPoint) throws Throwable { 43 | Method targetMethod = getTargetMethod(joinPoint); 44 | HttpServletRequest httpServletRequest = Objects.requireNonNull(WebUtils.getCurrentRequest()); 45 | 46 | //获取导出参数 47 | ExportParam exportParam = exportLifecycle.prepareParam(exportProperties, httpServletRequest); 48 | 49 | //准备导出上下文 50 | ExportContext exportContext = exportLifecycle.prepareContext(exportProperties, exportParam, targetMethod); 51 | 52 | try { 53 | //设置导出上下文信息 54 | ExportContextHolder.setContext(exportContext); 55 | 56 | //获取所有导出适配器 57 | Collection adapters = ExportAdapters.getAdapters(); 58 | 59 | //选择合适的适配器 60 | ExportAdapter exportAdapter = exportLifecycle.selectAdapter(exportContext, adapters); 61 | 62 | //生成导出字段信息 63 | exportLifecycle.prepareExportFieldInfos(exportContext, exportAdapter); 64 | 65 | //执行 66 | Object result = joinPoint.proceed(); 67 | 68 | //获取导出数据 69 | List list = exportLifecycle.prepareData(exportContext, result); 70 | 71 | //准备输出流 72 | OutputStream outputStream = exportLifecycle.prepareOutputStream(exportContext); 73 | 74 | //输出 75 | exportLifecycle.export(exportContext, list, exportAdapter, outputStream); 76 | 77 | //恢复 78 | exportLifecycle.reset(exportContext); 79 | 80 | //响应结果 81 | if (exportContext.isResponseResult()) { 82 | return result; 83 | } else { 84 | return null; 85 | } 86 | } finally { 87 | ExportContextHolder.clear(); 88 | } 89 | } 90 | 91 | /** 92 | * 获取当前请求执行方法 93 | * 94 | * @param proceedingJoinPoint 95 | * @return 96 | */ 97 | private static Method getTargetMethod(ProceedingJoinPoint proceedingJoinPoint) { 98 | return ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/core/lifecycle/AbstractExportLifeCycle.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.core.lifecycle; 2 | 3 | import com.gongbo.excel.common.enums.ExcelType; 4 | import com.gongbo.excel.common.utils.CollectionUtil; 5 | import com.gongbo.excel.common.utils.ResponseUtils; 6 | import com.gongbo.excel.common.utils.StringUtil; 7 | import com.gongbo.excel.common.utils.TemplateUtils; 8 | import com.gongbo.excel.export.annotations.Export; 9 | import com.gongbo.excel.export.annotations.Exports; 10 | import com.gongbo.excel.export.config.ExportProperties; 11 | import com.gongbo.excel.export.constants.ExportExcelType; 12 | import com.gongbo.excel.export.core.ExportHandlers; 13 | import com.gongbo.excel.export.core.resulthandler.ResultHandler; 14 | import com.gongbo.excel.export.entity.ExportContext; 15 | import com.gongbo.excel.export.exception.ExportFailedException; 16 | import com.gongbo.excel.export.exception.NotSupportExportException; 17 | 18 | import javax.servlet.http.HttpServletResponse; 19 | import java.lang.reflect.Method; 20 | import java.text.MessageFormat; 21 | import java.util.*; 22 | import java.util.function.Predicate; 23 | import java.util.stream.Collectors; 24 | 25 | public abstract class AbstractExportLifeCycle implements ExportLifecycle { 26 | 27 | private static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type"; 28 | private static final String HTTP_HEADER_CONTENT_DISPOSITION = "Content-Disposition"; 29 | private static final String BEFORE_EXPORT_RESPONSE_HEADER = "BEFORE_EXPORT_RESPONSE_HEADER"; 30 | 31 | /** 32 | * 根据exportTag获取对应Export注解 33 | * 34 | * @param exportTag 35 | * @param targetMethod 36 | * @return 37 | */ 38 | public static Export findExportAnnotation(String exportTag, Method targetMethod) { 39 | Export[] annotations = Optional.ofNullable(targetMethod.getAnnotation(Exports.class)) 40 | .map(Exports::value) 41 | .orElseGet(() -> targetMethod.getAnnotationsByType(Export.class)); 42 | 43 | //没有找到注解 44 | if (annotations == null || annotations.length == 0) { 45 | throw new NotSupportExportException(MessageFormat.format( 46 | "This method:{0} not support export, to enable export, please configure Export annotation on the request method to enable export!", 47 | targetMethod.getName())); 48 | } 49 | 50 | //根据exportGroup过滤 51 | Predicate filter; 52 | if (StringUtil.isNotEmpty(exportTag)) { 53 | filter = e -> exportTag.equals(e.tag()); 54 | } else { 55 | filter = e -> StringUtil.isEmpty(e.tag()); 56 | } 57 | 58 | List annotationList = Arrays.stream(annotations) 59 | .filter(filter) 60 | .collect(Collectors.toList()); 61 | 62 | //没有找到对应注解 63 | if (CollectionUtil.isEmpty(annotationList)) { 64 | throw new NotSupportExportException(MessageFormat.format("no matching export tag[{0}] on this method[{1}]", exportTag, targetMethod.getName())); 65 | } 66 | 67 | //多个注解匹配 68 | if (annotationList.size() > 1) { 69 | throw new ExportFailedException(MessageFormat.format("more than one export tag[{0}] matched on this method [{1}]", exportTag, targetMethod.getName())); 70 | } 71 | 72 | //返回匹配的注解 73 | return annotationList.get(0); 74 | } 75 | 76 | /** 77 | * 获取导出模型类 78 | * 79 | * @param targetMethod 80 | * @param export 81 | * @param exportProperties 82 | * @return 83 | */ 84 | protected static Class getModelClass(Method targetMethod, Export export, ExportProperties exportProperties, ResultHandler resultHandler) { 85 | //没有导出模型类 86 | if (export.modelClass() == Export.NoneModel.class) { 87 | return null; 88 | } 89 | //自动模型类时且是模板导出,则没有导出模型类 90 | else if (export.modelClass() == Export.AutoModel.class && StringUtil.isNotEmpty(export.template())) { 91 | return null; 92 | } 93 | //判断是否是自定义类 94 | else if (export.modelClass() == Export.AutoModel.class) { 95 | return Optional.ofNullable(resultHandler.getModelType(targetMethod)) 96 | .orElseThrow(() -> new IllegalArgumentException("unable to get the export model class, please check the export method or add the modelClass attribute to the Export annotation!")); 97 | } 98 | //根据方法返回类型查找 99 | else { 100 | return export.modelClass(); 101 | } 102 | } 103 | 104 | /** 105 | * 获取导出格式 106 | */ 107 | protected static ExcelType getExcelType(Export export, ExportProperties exportProperties) { 108 | if (export.excelType() != ExportExcelType.AUTO) { 109 | return export.excelType().getExcelType(); 110 | } 111 | if (StringUtil.isEmpty(export.template())) { 112 | String defaultExcelType = exportProperties.getDefaultExcelType(); 113 | if ("xls".equalsIgnoreCase(defaultExcelType)) { 114 | return ExcelType.XLS; 115 | } else { 116 | return ExcelType.XLSX; 117 | } 118 | } 119 | return TemplateUtils.getTemplateExcelType(export.template()); 120 | } 121 | 122 | /** 123 | * 获取文件名 124 | * 125 | * @param export 126 | * @return 127 | */ 128 | public static String buildFileName(Export export) { 129 | String name = ExportHandlers.of(export.fileNameConvert()) 130 | .apply(export.fileName()); 131 | if (StringUtil.isEmpty(name)) { 132 | name = String.valueOf(System.currentTimeMillis()); 133 | } 134 | return name; 135 | } 136 | 137 | /** 138 | * 设置响应头信息 139 | * 140 | * @param serverHttpResponse 141 | * @param exportContext 142 | */ 143 | public static void setDownloadResponseHeaders(HttpServletResponse serverHttpResponse, ExportContext exportContext) { 144 | String fileName = exportContext.getFileName(); 145 | String excelFileSuffix = exportContext.getExcelType().getValue(); 146 | ResponseUtils.setDownloadFileHeader(serverHttpResponse, fileName + excelFileSuffix); 147 | } 148 | 149 | /** 150 | * 获取默认的请求头信息 151 | * 152 | * @param serverHttpResponse 153 | * @return 154 | */ 155 | public static void storeResponseHeaders(ExportContext exportContext, HttpServletResponse serverHttpResponse) { 156 | Map headers = new HashMap<>(); 157 | headers.put(HTTP_HEADER_CONTENT_TYPE, serverHttpResponse.getHeader(HTTP_HEADER_CONTENT_TYPE)); 158 | if (serverHttpResponse.containsHeader(HTTP_HEADER_CONTENT_DISPOSITION)) { 159 | headers.put(HTTP_HEADER_CONTENT_DISPOSITION, serverHttpResponse.getHeader(HTTP_HEADER_CONTENT_DISPOSITION)); 160 | } 161 | exportContext.getUserContext().put(BEFORE_EXPORT_RESPONSE_HEADER, headers); 162 | } 163 | 164 | /** 165 | * @param serverHttpResponse 166 | */ 167 | public static void resetResponseHeaders(ExportContext exportContext, HttpServletResponse serverHttpResponse) { 168 | if (!exportContext.getUserContext().containsKey(BEFORE_EXPORT_RESPONSE_HEADER)) { 169 | return; 170 | } 171 | Map responseHeader = (Map) exportContext.getUserContext().get(BEFORE_EXPORT_RESPONSE_HEADER); 172 | serverHttpResponse.setHeader(HTTP_HEADER_CONTENT_TYPE, responseHeader.get(HTTP_HEADER_CONTENT_TYPE)); 173 | serverHttpResponse.setHeader(HTTP_HEADER_CONTENT_DISPOSITION, responseHeader.getOrDefault(HTTP_HEADER_CONTENT_DISPOSITION, null)); 174 | exportContext.getUserContext().remove(BEFORE_EXPORT_RESPONSE_HEADER); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/core/lifecycle/DefaultExportLifecycle.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.core.lifecycle; 2 | 3 | import com.gongbo.excel.common.enums.ExcelType; 4 | import com.gongbo.excel.common.utils.StringUtil; 5 | import com.gongbo.excel.common.utils.TemplateUtils; 6 | import com.gongbo.excel.common.utils.Utils; 7 | import com.gongbo.excel.common.utils.WebUtils; 8 | import com.gongbo.excel.export.adapter.ExportAdapter; 9 | import com.gongbo.excel.export.annotations.Export; 10 | import com.gongbo.excel.export.config.ExportProperties; 11 | import com.gongbo.excel.export.core.ExportHandlers; 12 | import com.gongbo.excel.export.custom.ExportDataConvert; 13 | import com.gongbo.excel.export.custom.FieldFilter; 14 | import com.gongbo.excel.export.entity.ExportContext; 15 | import com.gongbo.excel.export.entity.ExportFieldInfo; 16 | import com.gongbo.excel.export.param.ExportParam; 17 | import org.springframework.stereotype.Component; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | import java.io.*; 22 | import java.lang.reflect.Method; 23 | import java.nio.file.Files; 24 | import java.nio.file.Paths; 25 | import java.text.MessageFormat; 26 | import java.util.*; 27 | import java.util.stream.Collectors; 28 | 29 | @Component 30 | public class DefaultExportLifecycle extends AbstractExportLifeCycle implements ExportLifecycle { 31 | 32 | @Override 33 | public boolean isExportRequest(ExportProperties exportProperties, HttpServletRequest request) { 34 | return StringUtil.isNotEmpty(Utils.firstNotEmpty(() -> request.getParameter(ExportParam.EXPORT), 35 | () -> request.getHeader(ExportParam.EXPORT))); 36 | } 37 | 38 | @Override 39 | public ExportParam prepareParam(ExportProperties exportProperties, HttpServletRequest request) { 40 | String export = Utils.firstNotEmpty(() -> request.getParameter(ExportParam.EXPORT), 41 | () -> request.getHeader(ExportParam.EXPORT)); 42 | 43 | Objects.requireNonNull(export); 44 | 45 | ExportParam.Type type = ExportParam.Type.of(export); 46 | 47 | Objects.requireNonNull(type, MessageFormat.format("没有找到对应的导出类型:{0}", export)); 48 | 49 | //获取exportTag参数 50 | String exportTag = Utils.firstNotEmpty(() -> request.getParameter(ExportParam.EXPORT_TAG), 51 | () -> request.getHeader(ExportParam.EXPORT_TAG)); 52 | 53 | return ExportParam.builder() 54 | .exportTag(exportTag) 55 | .type(type) 56 | .build(); 57 | } 58 | 59 | @Override 60 | public ExportContext prepareContext(ExportProperties exportProperties, ExportParam exportParam, Method targetMethod) { 61 | //查找对应Export注解 62 | Export export = findExportAnnotation(exportParam.getExportTag(), targetMethod); 63 | 64 | //获取对应模型类 65 | Class modelClass = getModelClass(targetMethod, export, exportProperties, exportProperties.getResultHandler()); 66 | 67 | //获取导出文件名 68 | String fileName = buildFileName(export); 69 | 70 | //导出文件格式 71 | ExcelType excelType = getExcelType(export, exportProperties); 72 | 73 | //获取输出路径 74 | String outputPath = export.outputPath(); 75 | 76 | return ExportContext.builder() 77 | .exportProperties(exportProperties) 78 | .resultHandler(exportProperties.getResultHandler()) 79 | .model(modelClass) 80 | .export(export) 81 | .fileName(fileName) 82 | .sheetName(export.sheetName()) 83 | .template(export.template()) 84 | .excelType(excelType) 85 | .fieldInfos(null) 86 | .exportParam(exportParam) 87 | .outputPath(outputPath) 88 | .formula(export.formula()) 89 | .responseResult(export.responseResult()) 90 | .userContext(new HashMap<>()) 91 | .build(); 92 | } 93 | 94 | @Override 95 | public ExportAdapter selectAdapter(ExportContext exportContext, Collection adapters) { 96 | String exportBy = exportContext.getExport().exportBy(); 97 | 98 | if (StringUtil.isEmpty(exportBy)) { 99 | //取默认导出 100 | exportBy = exportContext.getExportProperties().getDefaultExportBy(); 101 | } 102 | 103 | for (ExportAdapter adapter : adapters) { 104 | if (exportBy.equals(adapter.name())) { 105 | return adapter; 106 | } 107 | } 108 | throw new IllegalArgumentException(MessageFormat.format("没有找到名为{0}的适配器", exportBy)); 109 | } 110 | 111 | @Override 112 | public void prepareExportFieldInfos(ExportContext exportContext, ExportAdapter exportAdapter) { 113 | Class modelClass = exportContext.getModel(); 114 | List fieldInfos; 115 | if (modelClass == null) { 116 | fieldInfos = Collections.emptyList(); 117 | } else { 118 | List exportFieldInfos = exportAdapter.findExportFieldInfos(exportContext.getModel()); 119 | //字段过滤器 120 | FieldFilter fieldFilter = ExportHandlers.of(exportContext.getExport().fieldFilter()); 121 | 122 | fieldInfos = exportFieldInfos 123 | .stream() 124 | .filter(fieldFilter::predict) 125 | .collect(Collectors.toList()); 126 | } 127 | 128 | exportContext.setFieldInfos(fieldInfos); 129 | } 130 | 131 | @Override 132 | public List prepareData(ExportContext exportContext, Object result) { 133 | ExportDataConvert exportDataConvert = ExportHandlers.of(exportContext.getExport().dataConvert()); 134 | return exportDataConvert.convert(exportContext, result); 135 | } 136 | 137 | @Override 138 | public OutputStream prepareOutputStream(ExportContext exportContext) throws IOException { 139 | if (exportContext.isOutputFile()) { 140 | Files.createDirectories(Paths.get(exportContext.getOutputPath())); 141 | File file = new File(exportContext.getOutputPath(), exportContext.getFileName() + exportContext.getExcelType().getValue()); 142 | return new FileOutputStream(file); 143 | } else { 144 | HttpServletResponse response = WebUtils.getCurrentResponse(); 145 | //保存当前响应头信息 146 | storeResponseHeaders(exportContext, response); 147 | //设置响应头信息 148 | setDownloadResponseHeaders(response, exportContext); 149 | return response.getOutputStream(); 150 | } 151 | } 152 | 153 | @Override 154 | public void export(ExportContext exportContext, List data, ExportAdapter adapter, OutputStream outputStream) throws IOException { 155 | String template = exportContext.getExport().template(); 156 | if (StringUtil.isNotEmpty(template)) { 157 | InputStream templateInputStream = TemplateUtils.getTemplateInputStream(exportContext.getExportProperties().getTemplateDir(), exportContext.getTemplate()); 158 | //模板导出 159 | adapter.export(exportContext, templateInputStream, data, outputStream); 160 | } else { 161 | //普通导出 162 | adapter.export(exportContext, data, outputStream); 163 | } 164 | } 165 | 166 | @Override 167 | public void reset(ExportContext exportContext) { 168 | //清除响应头信息 169 | resetResponseHeaders(exportContext, WebUtils.getCurrentResponse()); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/core/lifecycle/ExportLifecycle.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.core.lifecycle; 2 | 3 | import com.gongbo.excel.export.adapter.ExportAdapter; 4 | import com.gongbo.excel.export.config.ExportProperties; 5 | import com.gongbo.excel.export.entity.ExportContext; 6 | import com.gongbo.excel.export.param.ExportParam; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | import java.lang.reflect.Method; 12 | import java.util.Collection; 13 | import java.util.List; 14 | 15 | public interface ExportLifecycle { 16 | /** 17 | * 当前请求是否是导出请求 18 | * 19 | * @return 20 | */ 21 | boolean isExportRequest(ExportProperties exportProperties, HttpServletRequest request); 22 | 23 | /** 24 | * 导出参数提取 25 | * 26 | * @return 27 | */ 28 | ExportParam prepareParam(ExportProperties exportProperties, HttpServletRequest request); 29 | 30 | /** 31 | * 导出上下文 32 | * 33 | * @param exportParam 34 | * @param targetMethod 35 | * @return 36 | */ 37 | ExportContext prepareContext(ExportProperties exportProperties, ExportParam exportParam, Method targetMethod); 38 | 39 | /** 40 | * ExportAdapter选择 41 | * 42 | * @param exportContext 43 | * @param adapters 44 | * @return 45 | */ 46 | ExportAdapter selectAdapter(ExportContext exportContext, Collection adapters); 47 | 48 | /** 49 | * 列出导出字段信息列表 50 | */ 51 | void prepareExportFieldInfos(ExportContext exportContext, ExportAdapter exportAdapter); 52 | 53 | /** 54 | * 准备导出数据 55 | * 56 | * @param exportContext 57 | * @param result 58 | * @return 59 | */ 60 | List prepareData(ExportContext exportContext, Object result); 61 | 62 | 63 | /** 64 | * 准备输出流 65 | * 66 | * @param exportContext 67 | */ 68 | OutputStream prepareOutputStream(ExportContext exportContext) throws IOException; 69 | 70 | /** 71 | * 导出 72 | * 73 | * @param exportContext 74 | * @param data 75 | * @param outputStream 76 | */ 77 | void export(ExportContext exportContext, List data, ExportAdapter adapter, OutputStream outputStream) throws IOException; 78 | 79 | /** 80 | * 重置 81 | * 82 | * @param exportContext 83 | */ 84 | void reset(ExportContext exportContext); 85 | } 86 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/core/resulthandler/DefaultResultHandler.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.core.resulthandler; 2 | 3 | import com.gongbo.excel.export.utils.ExportUtils; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 5 | 6 | import java.lang.reflect.Method; 7 | import java.lang.reflect.ParameterizedType; 8 | import java.lang.reflect.Type; 9 | 10 | /** 11 | * 默认结果转换器 12 | * 13 | * @return 14 | */ 15 | @ConditionalOnMissingBean(ResultHandler.class) 16 | public class DefaultResultHandler implements ResultHandler { 17 | 18 | @Override 19 | public boolean checkResultType(Class returnType) { 20 | if (returnType.isArray() || Iterable.class.isAssignableFrom(returnType)) { 21 | return true; 22 | } 23 | Class resultClass = resultClass(); 24 | if (resultClass != null) { 25 | return resultClass.isAssignableFrom(returnType); 26 | } 27 | return false; 28 | } 29 | 30 | @Override 31 | public Class getModelType(Method method) { 32 | Class returnType = method.getReturnType(); 33 | 34 | //检查返回类型是否支持 35 | checkResultType(returnType); 36 | 37 | //如果不是包装类型(数组,集合,Iterable等) 38 | Type containerType; 39 | if (returnType.isArray()) { 40 | containerType = returnType; 41 | } else if (Iterable.class.isAssignableFrom(returnType)) { 42 | containerType = method.getGenericReturnType(); 43 | } else { 44 | Type genericReturnType = method.getGenericReturnType(); 45 | //不是泛型类型,则返回空 46 | if (!(genericReturnType instanceof ParameterizedType)) { 47 | return null; 48 | } 49 | ParameterizedType parameterizedReturnType = (ParameterizedType) genericReturnType; 50 | //获取泛型参数 51 | Type[] actualTypeArguments = parameterizedReturnType.getActualTypeArguments(); 52 | containerType = actualTypeArguments[0]; 53 | } 54 | return ExportUtils.getComponentType(containerType); 55 | } 56 | 57 | @Override 58 | public Class resultClass() { 59 | return null; 60 | } 61 | 62 | @Override 63 | public Object getResultData(Object result) { 64 | //集合或迭代器等直接返回 65 | if (result instanceof Iterable) { 66 | return result; 67 | } 68 | //数组则直接返回 69 | if (result != null && result.getClass().isArray()) { 70 | return result; 71 | } 72 | 73 | return null; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/core/resulthandler/ResultHandler.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.core.resulthandler; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | public interface ResultHandler { 6 | 7 | /** 8 | * 返回结果包装类 9 | * 10 | * @return 11 | */ 12 | Class resultClass(); 13 | 14 | /** 15 | * 检查返回结果 16 | * 17 | * @return 18 | */ 19 | boolean checkResultType(Class type); 20 | 21 | /** 22 | * 获取模型类型 23 | * 24 | * @return 25 | */ 26 | Class getModelType(Method method); 27 | 28 | /** 29 | * 从返回中获取结果数据 30 | * 31 | * @param result 32 | * @return 33 | */ 34 | Object getResultData(Object result); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/custom/ExportDataConvert.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.custom; 2 | 3 | 4 | import com.gongbo.excel.export.entity.ExportContext; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 导出数据转换 10 | */ 11 | public interface ExportDataConvert { 12 | 13 | /** 14 | * @param result 接口返回的数据( 15 | * @return 返回的是写入excel的数据 16 | */ 17 | List convert(ExportContext exportContext, Object result); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/custom/FieldFilter.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.custom; 2 | 3 | import com.gongbo.excel.export.entity.ExportFieldInfo; 4 | 5 | /** 6 | * 字段过滤 7 | */ 8 | public interface FieldFilter { 9 | 10 | /** 11 | * @param fieldInfo 12 | * @return 13 | */ 14 | boolean predict(ExportFieldInfo fieldInfo); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/custom/FileNameConvert.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.custom; 2 | 3 | /** 4 | * 文件名转换 5 | */ 6 | public interface FileNameConvert { 7 | 8 | /** 9 | * @param fileName 10 | * @return 11 | */ 12 | String apply(String fileName); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/custom/defaults/DefaultExportDataConvert.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.custom.defaults; 2 | 3 | import com.gongbo.excel.export.core.resulthandler.ResultHandler; 4 | import com.gongbo.excel.export.custom.ExportDataConvert; 5 | import com.gongbo.excel.export.entity.ExportContext; 6 | import com.gongbo.excel.export.utils.ExportUtils; 7 | 8 | import java.util.List; 9 | 10 | public class DefaultExportDataConvert implements ExportDataConvert { 11 | 12 | @Override 13 | public List convert(ExportContext exportContext, Object result) { 14 | ResultHandler resultHandler = exportContext.getResultHandler(); 15 | 16 | Object data = resultHandler.getResultData(result); 17 | if (data instanceof ExportDataConvert) { 18 | return ((ExportDataConvert) data).convert(exportContext, result); 19 | } 20 | 21 | return ExportUtils.objectToList(data); 22 | } 23 | } -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/custom/defaults/DefaultFieldFilter.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.custom.defaults; 2 | 3 | 4 | import com.gongbo.excel.export.custom.FieldFilter; 5 | import com.gongbo.excel.export.entity.ExportFieldInfo; 6 | 7 | public class DefaultFieldFilter implements FieldFilter { 8 | 9 | @Override 10 | public boolean predict(ExportFieldInfo fieldInfo) { 11 | return true; 12 | } 13 | } -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/custom/defaults/DefaultFileNameConvert.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.custom.defaults; 2 | 3 | 4 | import com.gongbo.excel.export.custom.FileNameConvert; 5 | 6 | public class DefaultFileNameConvert implements FileNameConvert { 7 | @Override 8 | public String apply(String fileName) { 9 | return fileName; 10 | } 11 | } -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/entity/ExportContext.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.entity; 2 | 3 | 4 | import com.gongbo.excel.common.enums.ExcelType; 5 | import com.gongbo.excel.common.utils.StringUtil; 6 | import com.gongbo.excel.export.annotations.Export; 7 | import com.gongbo.excel.export.config.ExportProperties; 8 | import com.gongbo.excel.export.core.resulthandler.ResultHandler; 9 | import com.gongbo.excel.export.param.ExportParam; 10 | import lombok.*; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | @Getter 17 | @Setter 18 | @Builder 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | public class ExportContext { 22 | 23 | /** 24 | * 导出请求参数 25 | */ 26 | private ExportParam exportParam; 27 | 28 | /** 29 | * 导出注解信息 30 | */ 31 | private Export export; 32 | 33 | /** 34 | * 导出对应模型类 35 | */ 36 | private Class model; 37 | 38 | /** 39 | * 导出文件名 40 | */ 41 | private String fileName; 42 | 43 | /** 44 | * 导出sheet名称 45 | */ 46 | private String sheetName; 47 | 48 | /** 49 | * 导出模板文件名称 50 | */ 51 | private String template; 52 | 53 | /** 54 | * excel文件格式 55 | */ 56 | private ExcelType excelType; 57 | 58 | /** 59 | * 输出文件地址 60 | */ 61 | private String outputPath; 62 | 63 | /** 64 | * 是否包含公式 65 | */ 66 | private boolean formula; 67 | 68 | /** 69 | * 70 | */ 71 | private boolean responseResult; 72 | 73 | /** 74 | * 导出字段信息 75 | */ 76 | private List fieldInfos; 77 | 78 | /** 79 | * 用户额外添加的信息 80 | */ 81 | private Map userContext; 82 | 83 | /** 84 | * 配置信息 85 | */ 86 | private ExportProperties exportProperties; 87 | 88 | /** 89 | * 90 | */ 91 | private ResultHandler resultHandler; 92 | 93 | /** 94 | * 是否输出目录 95 | * 96 | * @return 97 | */ 98 | public boolean isOutputFile() { 99 | return StringUtil.isNotEmpty(outputPath); 100 | } 101 | 102 | 103 | /** 104 | * 添加填充数据 105 | * 106 | * @param exportFillData 107 | */ 108 | public void addExportFillData(ExportFillData exportFillData) { 109 | listExportFillData().add(exportFillData); 110 | } 111 | 112 | /** 113 | * 获取填充数据 114 | * 115 | * @return 116 | */ 117 | public List listExportFillData() { 118 | return (List) userContext.computeIfAbsent(exportProperties.getFillKey(), _ignored -> new ArrayList<>()); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/entity/ExportFieldInfo.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.entity; 2 | 3 | 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | @Setter 9 | @Getter 10 | @Builder 11 | public class ExportFieldInfo { 12 | /** 13 | * 导出列名称 14 | */ 15 | private String name; 16 | 17 | /** 18 | * 对应实体字段名称 19 | */ 20 | private String fieldName; 21 | 22 | /** 23 | * 字段排序 24 | */ 25 | private int order; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/entity/ExportFillData.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.entity; 2 | 3 | 4 | import lombok.*; 5 | 6 | @Setter 7 | @Getter 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Builder 11 | public class ExportFillData { 12 | 13 | /** 14 | * 填充所有Sheet,默认false,为true时,将对所有sheet填充,此时sheetNo/sheetName将不起作用 15 | */ 16 | private boolean fillAllSheet = false; 17 | 18 | /** 19 | * 填充的sheet,从0开始 20 | */ 21 | private Integer sheetNo; 22 | 23 | /** 24 | * 填充的sheet 25 | */ 26 | private String sheetName; 27 | 28 | /** 29 | * 填充配置 30 | */ 31 | private Object fillConfig; 32 | 33 | /** 34 | * 填充数据 map/list/FillWrapper等类型 35 | */ 36 | private Object data; 37 | 38 | /** 39 | * @param data 40 | */ 41 | public ExportFillData(Object data) { 42 | this(data, false); 43 | } 44 | 45 | 46 | /** 47 | * @param data 48 | * @param fillAllSheet 49 | */ 50 | public ExportFillData(Object data, boolean fillAllSheet) { 51 | this.fillAllSheet = fillAllSheet; 52 | this.data = data; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/exception/ExportFailedException.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.exception; 2 | 3 | /** 4 | * 导出失败异常 5 | */ 6 | public class ExportFailedException extends RuntimeException { 7 | 8 | /** 9 | * 10 | */ 11 | public ExportFailedException() { 12 | super("export error"); 13 | } 14 | 15 | /** 16 | * @param message 17 | */ 18 | public ExportFailedException(String message) { 19 | super(message); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/exception/FillKeyNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.exception; 2 | 3 | import java.text.MessageFormat; 4 | 5 | public class FillKeyNotFoundException extends RuntimeException { 6 | 7 | /** 8 | * @param sheetNo 9 | * @param sheetName 10 | * @param key 11 | */ 12 | public FillKeyNotFoundException(Integer sheetNo, String sheetName, String key) { 13 | this(MessageFormat.format("on sheet(sheetNo:{0},sheetName:{1}) not found fill key:{2}", sheetNo, sheetName, key)); 14 | } 15 | 16 | /** 17 | * @param message 18 | */ 19 | public FillKeyNotFoundException(String message) { 20 | super(message); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/exception/NotSupportExportException.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.exception; 2 | 3 | /** 4 | * 方法不支持导出异常,即方法没有添加Export注解 5 | */ 6 | public class NotSupportExportException extends RuntimeException { 7 | 8 | /** 9 | * @param message 10 | */ 11 | public NotSupportExportException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/param/ExportParam.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.param; 2 | 3 | import lombok.*; 4 | 5 | @Setter 6 | @Getter 7 | @Builder 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class ExportParam { 11 | 12 | public static final String EXPORT = "export"; 13 | public static final String EXPORT_TAG = "export_tag"; 14 | 15 | /** 16 | * 导出请求分组 17 | */ 18 | private String exportTag; 19 | 20 | /** 21 | * 导出参数 22 | */ 23 | private Type type; 24 | 25 | 26 | /** 27 | * 是否是导出Excel文件 28 | * 29 | * @return 30 | */ 31 | public boolean isExcel() { 32 | return Type.EXPORT_EXCEL.equals(type); 33 | } 34 | 35 | @RequiredArgsConstructor 36 | public enum Type { 37 | EXPORT_EXCEL("excel"); 38 | 39 | private final String value; 40 | 41 | public static Type of(String value) { 42 | for (Type type : values()) { 43 | if (type.value.equals(value)) { 44 | return type; 45 | } 46 | } 47 | return null; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/utils/ExportFormulas.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 7 | public class ExportFormulas { 8 | 9 | /** 10 | * 求平均值公式 11 | */ 12 | private static final String AVERAGE_FORMULA_FORMAT = "=IF(ISERROR(AVERAGE(%s:%s)),\"\",AVERAGE(%s:%s))"; 13 | 14 | /** 15 | * 求和公式 16 | */ 17 | private static final String SUM_FORMULA_FORMAT = "=IF(ISERROR(SUM(%s:%s)),\"\",SUM(%s:%s))"; 18 | 19 | /** 20 | * 生成求平均值公式 21 | * 22 | * @param column 所在列 23 | * @param start 行起始坐标 24 | * @param end 行结束坐标 25 | */ 26 | public static String averageColumnFormula(String column, int start, int end) { 27 | String columnStart = column + start; 28 | String columnEnd = column + end; 29 | 30 | return String.format(AVERAGE_FORMULA_FORMAT, columnStart, columnEnd, columnStart, columnEnd); 31 | } 32 | 33 | /** 34 | * 生成求平均值公式 35 | * 36 | * @param row 所在行 37 | * @param start 列起始坐标 38 | * @param end 列结束坐标 39 | */ 40 | public static String averageRowFormula(int row, String start, String end) { 41 | String rowStart = row + start; 42 | String rowEnd = row + end; 43 | 44 | return String.format(AVERAGE_FORMULA_FORMAT, rowStart, rowEnd, rowStart, rowEnd); 45 | } 46 | 47 | /** 48 | * 生成求和值公式 49 | * 50 | * @param column 所在列 51 | * @param start 行起始坐标 52 | * @param end 行结束坐标 53 | */ 54 | public static String sumColumnFormula(String column, int start, int end) { 55 | String columnStart = column + start; 56 | String columnEnd = column + end; 57 | 58 | return String.format(SUM_FORMULA_FORMAT, columnStart, columnEnd, columnStart, columnEnd); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /easyexcel-plus-export/src/main/java/com/gongbo/excel/export/utils/ExportUtils.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.export.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.lang.reflect.ParameterizedType; 7 | import java.lang.reflect.Type; 8 | import java.lang.reflect.WildcardType; 9 | import java.util.*; 10 | 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public class ExportUtils { 13 | 14 | /** 15 | * 数组或集合类型转化为List类型 16 | * 17 | * @param result 18 | * @return 19 | */ 20 | public static List objectToList(Object result) { 21 | if (result == null) { 22 | return Collections.emptyList(); 23 | } 24 | 25 | if (result instanceof Collection) { 26 | if (result instanceof List) { 27 | return (List) result; 28 | } else { 29 | return new ArrayList<>((Collection) result); 30 | } 31 | } else if (result.getClass().isArray()) { 32 | return Arrays.asList((Object[]) result); 33 | } else if (result instanceof Iterable) { 34 | //支持返回Iterable 35 | Iterable iterable = (Iterable) result; 36 | List list = new ArrayList<>(); 37 | iterable.forEach(list::add); 38 | return list; 39 | } else { 40 | throw new IllegalArgumentException(); 41 | } 42 | } 43 | 44 | /** 45 | * 获取集合或数组的类型 46 | * 47 | * @param type 48 | * @return 49 | */ 50 | public static Class getComponentType(Type type) { 51 | //如果泛型参数是泛型类型 52 | if (type instanceof ParameterizedType) { 53 | ParameterizedType parameterizedType = (ParameterizedType) type; 54 | if (parameterizedType.getRawType() instanceof Class) { 55 | Class rawType = (Class) parameterizedType.getRawType(); 56 | if (Iterable.class.isAssignableFrom(rawType)) { 57 | Type actualType2 = parameterizedType.getActualTypeArguments()[0]; 58 | if (actualType2 instanceof Class) { 59 | return (Class) actualType2; 60 | } else if (actualType2 instanceof WildcardType) { 61 | return null; 62 | } 63 | } 64 | } 65 | } 66 | //如果泛型参数是泛数组类型 67 | else if (type instanceof Class && ((Class) type).isArray()) { 68 | return ((Class) type).getComponentType(); 69 | } 70 | return null; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /easyexcel-plus-import/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | easyexcel-plus-parent 7 | io.github.gongbox 8 | 2.4.1 9 | 10 | 4.0.0 11 | 12 | easyexcel-plus-import 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | io.github.gongbox 22 | easyexcel-plus-common 23 | ${project.parent.version} 24 | provided 25 | 26 | 27 | org.springframework 28 | spring-context-support 29 | provided 30 | 31 | 32 | org.springframework 33 | spring-aop 34 | provided 35 | 36 | 37 | org.aspectj 38 | aspectjweaver 39 | provided 40 | 41 | 42 | org.springframework 43 | spring-webmvc 44 | provided 45 | 46 | 47 | com.fasterxml.jackson.core 48 | jackson-databind 49 | provided 50 | 51 | 52 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/adapter/ImportAdapter.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.adapter; 2 | 3 | 4 | import com.gongbo.excel.common.adapter.Adapter; 5 | import com.gongbo.excel.imports.entity.ImportContext; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | import java.util.Collection; 11 | import java.util.concurrent.ExecutionException; 12 | import java.util.concurrent.TimeoutException; 13 | 14 | public interface ImportAdapter extends Adapter { 15 | 16 | /** 17 | * 读取数据 18 | * 19 | * @param importContext 20 | * @param inputStream 21 | * @return 22 | * @throws IOException 23 | */ 24 | Collection read(ImportContext importContext, InputStream inputStream) throws IOException, ExecutionException, InterruptedException, TimeoutException; 25 | 26 | /** 27 | * 响应模板 28 | * 29 | * @param importContext 30 | * @param outputStream 31 | */ 32 | void responseTemplate(ImportContext importContext, OutputStream outputStream) throws IOException; 33 | } 34 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/adapter/ImportAdapters.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.adapter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.util.Collection; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | 10 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 11 | public class ImportAdapters { 12 | private static final List ADAPTERS = new LinkedList<>(); 13 | 14 | /** 15 | * 添加导入适配器 16 | * 17 | * @param adapter 18 | */ 19 | public static void addAdapter(ImportAdapter adapter) { 20 | ADAPTERS.add(adapter); 21 | } 22 | 23 | /** 24 | * 获取所有导入适配器 25 | * 26 | * @return 27 | */ 28 | public static Collection getAdapters() { 29 | return ADAPTERS; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/advise/ImportAdvise.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.advise; 2 | 3 | 4 | import com.gongbo.excel.imports.config.ImportProperties; 5 | import com.gongbo.excel.imports.core.ImportProxy; 6 | import com.gongbo.excel.imports.core.lifecycle.DefaultImportLifecycle; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.aspectj.lang.ProceedingJoinPoint; 9 | import org.aspectj.lang.annotation.Around; 10 | import org.aspectj.lang.annotation.Aspect; 11 | import org.aspectj.lang.annotation.Pointcut; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | 14 | @Aspect 15 | @Slf4j 16 | public class ImportAdvise { 17 | 18 | @Autowired 19 | private ImportProperties importProperties; 20 | 21 | @Pointcut("@annotation(com.gongbo.excel.imports.annotations.Import)") 22 | public void doImport() { 23 | } 24 | 25 | @Around("doImport()") 26 | public Object around(ProceedingJoinPoint joinPoint) throws Throwable { 27 | ImportProxy importProxy = new ImportProxy(importProperties, new DefaultImportLifecycle()); 28 | //判断是否是导出 29 | if (importProxy.isImport()) { 30 | try { 31 | //执行导出 32 | return importProxy.proxy(joinPoint); 33 | } catch (Throwable e) { 34 | log.error("import error", e); 35 | throw e; 36 | } 37 | } 38 | return joinPoint.proceed(); 39 | } 40 | } -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/annotations/Import.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.annotations; 2 | 3 | 4 | import java.lang.annotation.*; 5 | 6 | @Target({ElementType.METHOD}) 7 | @Retention(RetentionPolicy.RUNTIME) 8 | @Documented 9 | public @interface Import { 10 | 11 | /** 12 | * 导入模板 13 | */ 14 | String template() default ""; 15 | 16 | /** 17 | * 导入模板下载文件名,否则为时间戳 18 | */ 19 | String templateFilename() default ""; 20 | 21 | /** 22 | * 导入sheet名称,为空时默认为Sheet1 23 | */ 24 | String sheetName() default ""; 25 | 26 | /** 27 | * 导入sheet位置,从0开始 28 | */ 29 | int sheetNo() default -1; 30 | 31 | /** 32 | * 请求参数名称 33 | */ 34 | String fileParam() default "file"; 35 | 36 | /** 37 | * 导入模型类 38 | */ 39 | Class model() default Object.class; 40 | 41 | /** 42 | * 指定导入方式,否则从配置中取默认值 43 | */ 44 | String importBy() default ""; 45 | } -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/annotations/ImportTarget.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.annotations; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 导入目标,标注在接收excel解析参数上 7 | */ 8 | @Target({ElementType.PARAMETER}) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Documented 11 | public @interface ImportTarget { 12 | } -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/config/ImportConfig.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.config; 2 | 3 | import com.gongbo.excel.imports.advise.ImportAdvise; 4 | import org.springframework.context.annotation.Import; 5 | import org.springframework.http.converter.HttpMessageConverter; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 | 8 | import java.util.List; 9 | 10 | @Import(ImportAdvise.class) 11 | public class ImportConfig implements WebMvcConfigurer { 12 | 13 | @Override 14 | public void extendMessageConverters(List> converters) { 15 | converters.add(new ImportMappingJackson2HttpMessageConverter()); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/config/ImportMappingJackson2HttpMessageConverter.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.springframework.http.MediaType; 5 | import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 6 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * 自定义HttpMessageConverter,支持以multipart/form-data格式(excel解析)传json数据 13 | */ 14 | public class ImportMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter { 15 | 16 | public ImportMappingJackson2HttpMessageConverter() { 17 | this(Jackson2ObjectMapperBuilder.json().build()); 18 | } 19 | 20 | public ImportMappingJackson2HttpMessageConverter(ObjectMapper objectMapper) { 21 | super(objectMapper); 22 | List supportedMediaTypes = new ArrayList<>(); 23 | supportedMediaTypes.add(MediaType.MULTIPART_FORM_DATA); 24 | this.setSupportedMediaTypes(supportedMediaTypes); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/config/ImportProperties.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.config; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import lombok.ToString; 6 | 7 | @Getter 8 | @Setter 9 | @ToString 10 | public class ImportProperties { 11 | /** 12 | * 默认Sheet名称 13 | */ 14 | private String defaultSheetName = "Sheet1"; 15 | 16 | /** 17 | * 模板文件路径 18 | */ 19 | private String templateDir = ""; 20 | 21 | /** 22 | * 默认导入方式 23 | */ 24 | private String defaultImportBy = "easy_excel"; 25 | 26 | /** 27 | * 读取excel超时时间(单位ms),不设置或设置为0时无读取时间限制 28 | */ 29 | private Integer readTimeout; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/core/ImportContextHolder.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.core; 2 | 3 | 4 | import com.gongbo.excel.imports.entity.ImportContext; 5 | import com.gongbo.excel.imports.param.ImportParam; 6 | import lombok.AccessLevel; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.Optional; 10 | 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public class ImportContextHolder { 13 | 14 | private static final ThreadLocal THREAD_LOCAL = new ThreadLocal<>(); 15 | 16 | /** 17 | * 当前接口是否正在执行导出 18 | * 19 | * @return 20 | */ 21 | public static boolean isImport() { 22 | return Optional.ofNullable(getContext()) 23 | .map(ImportContext::getImportParam) 24 | .map(ImportParam::isExcel) 25 | .orElse(false); 26 | } 27 | 28 | /** 29 | * 获取导出上下文 30 | * 31 | * @return 32 | */ 33 | public static ImportContext getContext() { 34 | return THREAD_LOCAL.get(); 35 | } 36 | 37 | /** 38 | * 设置导出上下文 39 | * 40 | * @param exportContext 41 | */ 42 | public static void setContext(ImportContext exportContext) { 43 | THREAD_LOCAL.set(exportContext); 44 | } 45 | 46 | /** 47 | * 清空 48 | */ 49 | public static void clear() { 50 | THREAD_LOCAL.remove(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/core/ImportProxy.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.core; 2 | 3 | import com.gongbo.excel.common.utils.WebUtils; 4 | import com.gongbo.excel.imports.adapter.ImportAdapter; 5 | import com.gongbo.excel.imports.adapter.ImportAdapters; 6 | import com.gongbo.excel.imports.config.ImportProperties; 7 | import com.gongbo.excel.imports.core.lifecycle.ImportLifecycle; 8 | import com.gongbo.excel.imports.entity.ImportContext; 9 | import com.gongbo.excel.imports.param.ImportParam; 10 | import lombok.RequiredArgsConstructor; 11 | import org.aspectj.lang.ProceedingJoinPoint; 12 | import org.aspectj.lang.reflect.MethodSignature; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import java.lang.reflect.Method; 16 | import java.util.Collection; 17 | import java.util.Objects; 18 | 19 | @RequiredArgsConstructor 20 | public class ImportProxy { 21 | 22 | private final ImportProperties importProperties; 23 | private final ImportLifecycle importLifecycle; 24 | 25 | /** 26 | * 检测是否导出 27 | */ 28 | public boolean isImport() { 29 | return importLifecycle.isImportRequest(importProperties, WebUtils.getCurrentRequest()); 30 | } 31 | 32 | public Object proxy(ProceedingJoinPoint joinPoint) throws Throwable { 33 | Method targetMethod = getTargetMethod(joinPoint); 34 | HttpServletRequest httpServletRequest = Objects.requireNonNull(WebUtils.getCurrentRequest()); 35 | 36 | //获取导出参数 37 | ImportParam importParam = importLifecycle.prepareParam(importProperties, httpServletRequest); 38 | 39 | //准备导出上下文 40 | ImportContext importContext = importLifecycle.prepareContext(importProperties, importParam, targetMethod); 41 | 42 | try { 43 | //设置导出上下文信息 44 | ImportContextHolder.setContext(importContext); 45 | //获取所有导出适配器 46 | Collection adapters = ImportAdapters.getAdapters(); 47 | //选择合适的适配器 48 | ImportAdapter importAdapter = importLifecycle.selectAdapter(importContext, adapters); 49 | if (importParam.isTemplate()) { 50 | //响应模板 51 | importLifecycle.responseTemplate(importContext, importAdapter); 52 | return null; 53 | } else if (importParam.isExcel()) { 54 | //获取导出数据 55 | Collection list = importLifecycle.readData(importContext, importAdapter); 56 | //转换结果 57 | Object result = importLifecycle.convertData(importContext, list); 58 | //修改方法参数列表 59 | Object[] args = importLifecycle.buildArguments(importContext, joinPoint, result); 60 | //执行方法 61 | return joinPoint.proceed(args); 62 | } else { 63 | throw new UnsupportedOperationException(); 64 | } 65 | } finally { 66 | ImportContextHolder.clear(); 67 | } 68 | } 69 | 70 | /** 71 | * 获取当前请求执行方法 72 | * 73 | * @param proceedingJoinPoint 74 | * @return 75 | */ 76 | private static Method getTargetMethod(ProceedingJoinPoint proceedingJoinPoint) { 77 | return ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/core/lifecycle/DefaultImportLifecycle.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.core.lifecycle; 2 | 3 | import com.gongbo.excel.common.enums.ExcelType; 4 | import com.gongbo.excel.common.utils.*; 5 | import com.gongbo.excel.imports.adapter.ImportAdapter; 6 | import com.gongbo.excel.imports.annotations.Import; 7 | import com.gongbo.excel.imports.config.ImportProperties; 8 | import com.gongbo.excel.imports.entity.ImportContext; 9 | import com.gongbo.excel.imports.exception.NotSupportImportException; 10 | import com.gongbo.excel.imports.param.ImportParam; 11 | import com.gongbo.excel.imports.utils.ImportUtils; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.aspectj.lang.ProceedingJoinPoint; 14 | import org.springframework.util.FileCopyUtils; 15 | 16 | import javax.servlet.ServletException; 17 | import javax.servlet.http.HttpServletRequest; 18 | import javax.servlet.http.HttpServletResponse; 19 | import javax.servlet.http.Part; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.lang.reflect.Array; 23 | import java.lang.reflect.Method; 24 | import java.lang.reflect.Modifier; 25 | import java.text.MessageFormat; 26 | import java.util.*; 27 | import java.util.concurrent.ExecutionException; 28 | import java.util.concurrent.TimeoutException; 29 | import java.util.stream.Collectors; 30 | 31 | import static com.gongbo.excel.imports.param.ImportParam.IMPORT; 32 | 33 | 34 | @Slf4j 35 | public class DefaultImportLifecycle implements ImportLifecycle { 36 | /** 37 | * 当前请求是否是导入请求 38 | * 39 | * @return true:是,false:否 40 | */ 41 | @Override 42 | public boolean isImportRequest(ImportProperties importProperties, HttpServletRequest request) { 43 | return StringUtil.isNotEmpty(Utils.firstNotEmpty(() -> request.getParameter(IMPORT), () -> request.getHeader(IMPORT))); 44 | } 45 | 46 | /** 47 | * 导入参数提取 48 | * 49 | * @return 导入参数 50 | */ 51 | @Override 52 | public ImportParam prepareParam(ImportProperties importProperties, HttpServletRequest request) { 53 | String param = Utils.firstNotEmpty(() -> request.getParameter(IMPORT), () -> request.getHeader(IMPORT)); 54 | 55 | Objects.requireNonNull(param); 56 | 57 | ImportParam.Type type = ImportParam.Type.of(param); 58 | 59 | Objects.requireNonNull(type); 60 | 61 | return ImportParam.builder() 62 | .type(type) 63 | .build(); 64 | } 65 | 66 | /** 67 | * 构建导出上下文 68 | * 69 | * @param importParam 70 | * @param targetMethod 71 | * @return 导出上下文 72 | */ 73 | @Override 74 | public ImportContext prepareContext(ImportProperties importProperties, ImportParam importParam, Method targetMethod) { 75 | Import importAnnotation = targetMethod.getAnnotation(Import.class); 76 | //检查是否支持导入 77 | if (importAnnotation == null) { 78 | throw new NotSupportImportException(MessageFormat.format("this method:{0} not support import, to enable import, please configure Import annotation on the request method to enable import", targetMethod.getName())); 79 | } 80 | 81 | //目标参数位置 82 | boolean mustExists = !importParam.isTemplate() || importAnnotation.model() == Object.class; 83 | Integer argIndex = ImportUtils.getImportTargetArgIndex(targetMethod, mustExists); 84 | //目标参数类型 85 | Class modelContainerClass = argIndex == null ? null : ImportUtils.getModelContainerClass(targetMethod, argIndex); 86 | //导入数据模型类 87 | Class modelClass; 88 | if (importAnnotation.model() != Object.class) { 89 | modelClass = importAnnotation.model(); 90 | } else { 91 | modelClass = ImportUtils.getModelClass(targetMethod, argIndex); 92 | 93 | if (modelClass == null) { 94 | throw new IllegalArgumentException("unable to get the import model class, please check the import method or add the modelClass attribute to the Import annotation!"); 95 | } 96 | } 97 | 98 | String sheetName = importAnnotation.sheetName(); 99 | 100 | return ImportContext.builder() 101 | .importProperties(importProperties) 102 | .template(importAnnotation.template()) 103 | .templateFilename(importAnnotation.templateFilename()) 104 | .importParam(importParam) 105 | .anImport(importAnnotation) 106 | .fileParam(importAnnotation.fileParam()) 107 | .sheetNo(importAnnotation.sheetNo() >= 0 ? importAnnotation.sheetNo() : null) 108 | .sheetName(sheetName) 109 | .targetArgumentIndex(argIndex) 110 | .targetArgumentContainerClass(modelContainerClass) 111 | .targetArgumentClass(modelClass) 112 | .build(); 113 | } 114 | 115 | /** 116 | * ImportAdapter选择 117 | * 118 | * @param importContext 119 | * @param adapters 120 | * @return 121 | */ 122 | @Override 123 | public ImportAdapter selectAdapter(ImportContext importContext, Collection adapters) { 124 | String importBy = importContext.getAnImport().importBy(); 125 | 126 | if (StringUtil.isEmpty(importBy)) { 127 | //取默认导出 128 | importBy = importContext.getImportProperties().getDefaultImportBy(); 129 | } 130 | 131 | for (ImportAdapter adapter : adapters) { 132 | if (importBy.equals(adapter.name())) { 133 | return adapter; 134 | } 135 | } 136 | throw new IllegalArgumentException(MessageFormat.format("没有找到名为{0}的适配器", importBy)); 137 | } 138 | 139 | /** 140 | * 响应模板文件 141 | */ 142 | @Override 143 | public void responseTemplate(ImportContext importContext, ImportAdapter importAdapter) throws IOException { 144 | HttpServletResponse response = WebUtils.getCurrentResponse(); 145 | 146 | String responseFilename = importContext.getTemplateFilename(); 147 | if (StringUtil.isEmpty(responseFilename)) { 148 | responseFilename = String.valueOf(System.currentTimeMillis()); 149 | } 150 | 151 | if (StringUtil.isNotEmpty(importContext.getTemplate())) { 152 | //设置文件后缀 153 | responseFilename += TemplateUtils.getTemplateExcelType(importContext.getTemplate()).getValue(); 154 | //设置响应头信息 155 | ResponseUtils.setDownloadFileHeader(response, responseFilename); 156 | InputStream templateInputStream = TemplateUtils.getTemplateInputStream(importContext.getImportProperties().getTemplateDir(), 157 | importContext.getTemplateFilename()); 158 | FileCopyUtils.copy(templateInputStream, response.getOutputStream()); 159 | } else { 160 | //设置文件后缀 161 | responseFilename += ExcelType.XLSX.getValue(); 162 | //设置响应头信息 163 | ResponseUtils.setDownloadFileHeader(response, responseFilename); 164 | importAdapter.responseTemplate(importContext, response.getOutputStream()); 165 | } 166 | } 167 | 168 | /** 169 | * 读取数据 170 | * 171 | * @param importContext 172 | * @param importAdapter 173 | * @return 174 | */ 175 | @Override 176 | public Collection readData(ImportContext importContext, ImportAdapter importAdapter) throws IOException, ExecutionException, InterruptedException, TimeoutException, ServletException { 177 | Part file = WebUtils.getCurrentRequest().getPart(importContext.getFileParam()); 178 | 179 | if (file == null) { 180 | throw new IllegalArgumentException("没有读取到文件参数"); 181 | } 182 | 183 | //获取导入数据 184 | return importAdapter.read(importContext, file.getInputStream()); 185 | } 186 | 187 | /** 188 | * 数据转换 189 | * 190 | * @param importContext 191 | * @param data 192 | * @return 193 | */ 194 | @Override 195 | public Object convertData(ImportContext importContext, Collection data) { 196 | if (data == null) { 197 | return null; 198 | } 199 | 200 | // 201 | Class containerClass = importContext.getTargetArgumentContainerClass(); 202 | 203 | //如果当前数据类型已经是要求的类型的子类型,则直接返回当前数据 204 | if (containerClass.isAssignableFrom(data.getClass())) { 205 | return data; 206 | } 207 | 208 | //如果当前数据类型是数组类型 209 | if (containerClass.isArray()) { 210 | //将集合转换为数组返回 211 | return data.stream().toArray(value -> (Object[]) Array.newInstance(containerClass.getComponentType(), value)); 212 | } 213 | 214 | //如果当前数据类型是集合类型 215 | if (Collection.class.isAssignableFrom(containerClass)) { 216 | //当前容器类型是接口类型 217 | if (containerClass.isInterface()) { 218 | //Set类型 219 | if (Set.class.isAssignableFrom(containerClass)) { 220 | return new HashSet<>(data); 221 | } 222 | //List类型 223 | if (List.class.isAssignableFrom(containerClass)) { 224 | return new ArrayList<>(data); 225 | } 226 | //其他类型待补充 227 | } 228 | //如果不是抽象类型 229 | if ((containerClass.getModifiers() & Modifier.ABSTRACT) == 0) { 230 | //new 一个实例出来 231 | try { 232 | Collection collection = (Collection) containerClass.newInstance(); 233 | return data.stream().collect(Collectors.toCollection(() -> collection)); 234 | } catch (InstantiationException | IllegalAccessException e) { 235 | log.error("导入生成接口参数实例错误", e); 236 | } 237 | } 238 | } 239 | 240 | //数据转换失败 241 | throw new IllegalArgumentException(); 242 | } 243 | 244 | /** 245 | * 生成请求参数 246 | * 247 | * @param importContext 248 | * @param joinPoint 249 | * @param data 250 | * @return 251 | */ 252 | @Override 253 | public Object[] buildArguments(ImportContext importContext, ProceedingJoinPoint joinPoint, Object data) { 254 | Object[] args = joinPoint.getArgs(); 255 | //替换对应位置参数 256 | args[importContext.getTargetArgumentIndex()] = data; 257 | return args; 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/core/lifecycle/ImportLifecycle.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.core.lifecycle; 2 | 3 | import com.gongbo.excel.imports.adapter.ImportAdapter; 4 | import com.gongbo.excel.imports.config.ImportProperties; 5 | import com.gongbo.excel.imports.entity.ImportContext; 6 | import com.gongbo.excel.imports.param.ImportParam; 7 | import org.aspectj.lang.ProceedingJoinPoint; 8 | 9 | import javax.servlet.ServletException; 10 | import javax.servlet.http.HttpServletRequest; 11 | import java.io.IOException; 12 | import java.lang.reflect.Method; 13 | import java.util.Collection; 14 | import java.util.concurrent.ExecutionException; 15 | import java.util.concurrent.TimeoutException; 16 | 17 | public interface ImportLifecycle { 18 | 19 | /** 20 | * 当前请求是否是导入请求 21 | * 22 | * @return 23 | */ 24 | boolean isImportRequest(ImportProperties importProperties, HttpServletRequest request); 25 | 26 | /** 27 | * 导入参数提取 28 | * 29 | * @return 导入参数 30 | */ 31 | ImportParam prepareParam(ImportProperties importProperties, HttpServletRequest request); 32 | 33 | /** 34 | * 构建导出上下文 35 | * 36 | * @param importParam 37 | * @param targetMethod 38 | * @return 导出上下文 39 | */ 40 | ImportContext prepareContext(ImportProperties importProperties, ImportParam importParam, Method targetMethod); 41 | 42 | /** 43 | * ImportAdapter选择 44 | * 45 | * @param importContext 46 | * @param adapters 47 | * @return 48 | */ 49 | ImportAdapter selectAdapter(ImportContext importContext, Collection adapters); 50 | 51 | /** 52 | * 响应模板文件 53 | */ 54 | void responseTemplate(ImportContext importContext, ImportAdapter importAdapter) throws IOException; 55 | 56 | /** 57 | * 读取数据 58 | * 59 | * @param importContext 60 | * @param importAdapter 61 | * @return 62 | */ 63 | Collection readData(ImportContext importContext, ImportAdapter importAdapter) throws IOException, ExecutionException, InterruptedException, TimeoutException, ServletException; 64 | 65 | /** 66 | * 数据转换 67 | * 68 | * @param importContext 69 | * @param data 70 | * @return 71 | */ 72 | Object convertData(ImportContext importContext, Collection data); 73 | 74 | /** 75 | * 生成请求参数 76 | * 77 | * @param importContext 78 | * @param joinPoint 79 | * @param data 80 | * @return 81 | */ 82 | Object[] buildArguments(ImportContext importContext, ProceedingJoinPoint joinPoint, Object data); 83 | 84 | } 85 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/entity/ImportContext.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.entity; 2 | 3 | 4 | import com.gongbo.excel.imports.annotations.Import; 5 | import com.gongbo.excel.imports.config.ImportProperties; 6 | import com.gongbo.excel.imports.param.ImportParam; 7 | import lombok.*; 8 | 9 | @Getter 10 | @Setter 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class ImportContext { 15 | 16 | /** 17 | * 导入请求参数 18 | */ 19 | private ImportParam importParam; 20 | 21 | /** 22 | * 导入注解信息 23 | */ 24 | private Import anImport; 25 | 26 | /** 27 | * 导入模板 28 | */ 29 | private String template; 30 | 31 | /** 32 | * 导入模板文件名 33 | */ 34 | private String templateFilename; 35 | 36 | /** 37 | * 导入sheet名称 38 | */ 39 | private String sheetName; 40 | 41 | /** 42 | * 导入sheet位置 43 | */ 44 | private Integer sheetNo; 45 | 46 | /** 47 | * 导入目标参数位置 48 | */ 49 | private Integer targetArgumentIndex; 50 | 51 | /** 52 | * 导入接收参数容器类型 53 | */ 54 | private Class targetArgumentContainerClass; 55 | 56 | /** 57 | * 导入对应模型类 58 | */ 59 | private Class targetArgumentClass; 60 | 61 | /** 62 | * 导入文件参数名称 63 | */ 64 | private String fileParam; 65 | 66 | /** 67 | * 配置信息 68 | */ 69 | private ImportProperties importProperties; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/exception/ImportFailedException.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.exception; 2 | 3 | /** 4 | * 导出失败异常 5 | */ 6 | public class ImportFailedException extends RuntimeException { 7 | 8 | /** 9 | * @param message 10 | */ 11 | public ImportFailedException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/exception/NotSupportImportException.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.exception; 2 | 3 | /** 4 | * 方法不支持导出异常,即方法没有添加Import注解 5 | */ 6 | public class NotSupportImportException extends RuntimeException { 7 | 8 | /** 9 | * @param message 10 | */ 11 | public NotSupportImportException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/param/ImportParam.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.param; 2 | 3 | import lombok.*; 4 | 5 | @Setter 6 | @Getter 7 | @Builder 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class ImportParam { 11 | 12 | public static final String IMPORT = "import"; 13 | 14 | /** 15 | * 导出参数 16 | */ 17 | private Type type; 18 | 19 | /** 20 | * 是否是获取导入模板 21 | */ 22 | public boolean isTemplate() { 23 | return Type.IMPORT_TEMPLATE.equals(type); 24 | } 25 | 26 | /** 27 | * 是否是导入数据 28 | */ 29 | public boolean isExcel() { 30 | return Type.IMPORT_EXCEL.equals(type); 31 | } 32 | 33 | @RequiredArgsConstructor 34 | public enum Type { 35 | IMPORT_TEMPLATE("template"), 36 | IMPORT_EXCEL("excel"); 37 | 38 | private final String value; 39 | 40 | public static Type of(String value) { 41 | for (Type type : values()) { 42 | if (type.value.equals(value)) { 43 | return type; 44 | } 45 | } 46 | return null; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /easyexcel-plus-import/src/main/java/com/gongbo/excel/imports/utils/ImportUtils.java: -------------------------------------------------------------------------------- 1 | package com.gongbo.excel.imports.utils; 2 | 3 | 4 | import com.gongbo.excel.imports.annotations.ImportTarget; 5 | import lombok.AccessLevel; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.lang.reflect.*; 10 | import java.util.*; 11 | 12 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 13 | public class ImportUtils { 14 | 15 | /** 16 | * 获取参数位置 17 | */ 18 | public static Integer getImportTargetArgIndex(Method method, boolean mustExists) { 19 | int parameterCount = method.getParameterCount(); 20 | 21 | int argIndex = -1; 22 | 23 | if (parameterCount == 1) { 24 | argIndex = 0; 25 | } else if (parameterCount > 1) { 26 | Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 27 | for (int i = 0; i < parameterAnnotations.length; i++) { 28 | if (parameterAnnotations[i] != null && parameterAnnotations[i].length > 0) { 29 | for (Annotation annotation : parameterAnnotations[i]) { 30 | if (annotation.annotationType() == ImportTarget.class) { 31 | argIndex = i; 32 | break; 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | //检查 40 | if (argIndex < 0) { 41 | if (mustExists) { 42 | throw new IllegalArgumentException("not found import argument"); 43 | } 44 | return null; 45 | } 46 | 47 | if (argIndex >= method.getParameterCount()) { 48 | throw new IllegalArgumentException("find import argument error"); 49 | } 50 | 51 | return argIndex; 52 | } 53 | 54 | /** 55 | * 获取参数的参数类型 56 | */ 57 | public static Class getModelClass(Method method, Integer argIndex) { 58 | Type[] parameterTypes = method.getGenericParameterTypes(); 59 | return getModelTypeClass(parameterTypes[argIndex]); 60 | } 61 | 62 | /** 63 | * 获取参数的容器类型 64 | */ 65 | public static Class getModelContainerClass(Method method, Integer argIndex) { 66 | Class[] parameterTypes = method.getParameterTypes(); 67 | return parameterTypes[argIndex]; 68 | } 69 | 70 | 71 | /** 72 | * 构建容器 73 | */ 74 | public static Collection buildCollectionContainer(Class containerClass) { 75 | //如果当前数据类型是集合类型 76 | if (Collection.class.isAssignableFrom(containerClass)) { 77 | //当前容器类型是接口类型 78 | if (containerClass.isInterface()) { 79 | //Set类型 80 | if (Set.class.isAssignableFrom(containerClass)) { 81 | return new HashSet<>(); 82 | } 83 | //List类型 84 | if (List.class.isAssignableFrom(containerClass)) { 85 | return new ArrayList<>(); 86 | } 87 | //其他类型待补充 88 | } 89 | //如果不是抽象类型 90 | if ((containerClass.getModifiers() & Modifier.ABSTRACT) == 0) { 91 | //new 一个实例出来 92 | try { 93 | return (Collection) containerClass.newInstance(); 94 | } catch (InstantiationException | IllegalAccessException e) { 95 | throw new RuntimeException(e); 96 | } 97 | } 98 | } 99 | 100 | //默认返回ArrayList 101 | return new ArrayList<>(); 102 | } 103 | 104 | /** 105 | * 获取集合类型 106 | */ 107 | private static Class getModelTypeClass(Type parameterType) { 108 | if (parameterType instanceof ParameterizedType) { 109 | ParameterizedType parameterizedType = (ParameterizedType) parameterType; 110 | //获取泛型参数 111 | if (parameterizedType.getRawType() instanceof Class) { 112 | Class rawClass = (Class) parameterizedType.getRawType(); 113 | if (Iterable.class.isAssignableFrom(rawClass)) { 114 | Type actualType = parameterizedType.getActualTypeArguments()[0]; 115 | if (actualType instanceof Class) { 116 | return (Class) actualType; 117 | } else if (actualType instanceof WildcardType) { 118 | //暂时不支持 119 | return null; 120 | } 121 | } 122 | } 123 | } //如果参数是数组类型 124 | else if (parameterType instanceof Class && ((Class) parameterType).isArray()) { 125 | return ((Class) parameterType).getComponentType(); 126 | } 127 | 128 | throw new IllegalArgumentException(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /easyexcel-plus/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | easyexcel-plus-parent 7 | io.github.gongbox 8 | 2.4.1 9 | 10 | 4.0.0 11 | 12 | easyexcel-plus 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | io.github.gongbox 22 | easyexcel-plus-adapter 23 | ${project.parent.version} 24 | compile 25 | 26 | 27 | -------------------------------------------------------------------------------- /images/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongbox/easyexcel-plus/06a1b93a9da024d077610d771692d5496ceaad06/images/img.png -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongbox/easyexcel-plus/06a1b93a9da024d077610d771692d5496ceaad06/img.png -------------------------------------------------------------------------------- /img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongbox/easyexcel-plus/06a1b93a9da024d077610d771692d5496ceaad06/img_1.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.gongbox 8 | easyexcel-plus-parent 9 | pom 10 | 2.4.1 11 | easyexcel-plus 12 | easyexcel-plus 13 | https://github.com/gongbox/easyexcel-plus 14 | 15 | easyexcel-plus-common 16 | easyexcel-plus-export 17 | easyexcel-plus-import 18 | easyexcel-plus-adapter 19 | easyexcel-plus 20 | easyexcel-plus-boot-starter 21 | easyexcel-plus-boot-starter-test 22 | 23 | 24 | 25 | 8 26 | 8 27 | 28 | 5.3.14 29 | 2.6.2 30 | 2.13.1 31 | 3.0.5 32 | 3.0.0 33 | 1.18.20 34 | 35 | 36 | 37 | 38 | 39 | org.springframework 40 | spring-context-support 41 | ${spring.version} 42 | 43 | 44 | org.springframework 45 | spring-web 46 | ${spring.version} 47 | 48 | 49 | org.springframework 50 | spring-webmvc 51 | ${spring.version} 52 | 53 | 54 | org.springframework 55 | spring-aop 56 | ${spring.version} 57 | 58 | 59 | org.springframework 60 | spring-aspects 61 | ${spring.version} 62 | 63 | 64 | org.aspectj 65 | aspectjweaver 66 | 1.9.7 67 | 68 | 69 | javax.servlet 70 | javax.servlet-api 71 | 4.0.1 72 | 73 | 74 | 75 | com.alibaba 76 | easyexcel 77 | ${easyexcel.version} 78 | 79 | 80 | 81 | org.springframework.boot 82 | spring-boot-autoconfigure 83 | ${spring-boot.version} 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-configuration-processor 88 | ${spring-boot.version} 89 | 90 | 91 | org.springframework.boot 92 | spring-boot-starter-web 93 | ${spring-boot.version} 94 | 95 | 96 | 97 | com.fasterxml.jackson.core 98 | jackson-annotations 99 | ${jackson.version} 100 | 101 | 102 | 103 | com.fasterxml.jackson.core 104 | jackson-databind 105 | ${jackson.version} 106 | 107 | 108 | 109 | io.springfox 110 | springfox-swagger2 111 | ${swagger.version} 112 | 113 | 114 | io.springfox 115 | springfox-swagger-ui 116 | ${swagger.version} 117 | 118 | 119 | 120 | cn.hutool 121 | hutool-core 122 | 5.7.19 123 | 124 | 125 | 126 | org.slf4j 127 | slf4j-api 128 | 1.7.26 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | org.projectlombok 137 | lombok 138 | ${lombok.version} 139 | provided 140 | 141 | 142 | 143 | 144 | 145 | The Apache Software License, Version 2.0 146 | http://www.apache.org/licenses/LICENSE-2.0.txt 147 | 148 | 149 | 150 | scm:git:https://github.com/gongbox/easyexcel-plus.git 151 | scm:git:https://github.com/gongbox/easyexcel-plus.git 152 | scm:git:https://http://github.com/gongbox/easyexcel-plus 153 | 154 | 155 | 156 | gongbo 157 | 1326114523@qq.com 158 | 159 | 160 | 161 | 162 | 163 | release 164 | 165 | 166 | 167 | src/main/java 168 | 169 | **/* 170 | 171 | 172 | 173 | 174 | 175 | 176 | org.apache.maven.plugins 177 | maven-source-plugin 178 | 2.2.1 179 | 180 | 181 | package 182 | 183 | jar-no-fork 184 | 185 | 186 | 187 | 188 | 189 | 190 | org.apache.maven.plugins 191 | maven-javadoc-plugin 192 | 2.9.1 193 | 194 | private 195 | true 196 | UTF-8 197 | UTF-8 198 | UTF-8 199 | -Xdoclint:none 200 | 201 | 202 | 203 | 204 | package 205 | 206 | jar 207 | 208 | 209 | 210 | 211 | 212 | 213 | org.apache.maven.plugins 214 | maven-gpg-plugin 215 | 1.5 216 | 217 | 218 | verify 219 | 220 | sign 221 | 222 | 223 | 224 | 225 | 226 | 227 | org.apache.maven.plugins 228 | maven-compiler-plugin 229 | 3.0 230 | 231 | 1.8 232 | 1.8 233 | true 234 | true 235 | UTF-8 236 | false 237 | 238 | 239 | 240 | 241 | org.apache.maven.plugins 242 | maven-release-plugin 243 | 2.5.3 244 | 245 | v@{project.version} 246 | true 247 | false 248 | 249 | 250 | 251 | org.codehaus.mojo 252 | versions-maven-plugin 253 | 2.3 254 | 255 | false 256 | 257 | 258 | 259 | 260 | 261 | 262 | ossrh 263 | Sonatype Nexus Snapshots 264 | https://s01.oss.sonatype.org/content/repositories/snapshots/ 265 | 266 | 267 | ossrh 268 | Nexus Release Repository 269 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 270 | 271 | 272 | 273 | 274 | --------------------------------------------------------------------------------