封装的easyexcel,基于注解实现excel的导入导出,以场景来说,就是你有一个现成的分页接口或者一个list接口,只需要添加几个简单的注解,就可以实现excel的导出,也是为了方便有模板生成代码的情况下直接生成导出功能。
这是封装的依赖库源码:https://github.com/chenqi92/allbs-excel
这是这个依赖库的使用示例:https://github.com/chenqi92/allbs-excel-test
依赖库运行后在浏览器中打开:http://localhost:8080/ 即可测试各种示例,参照示例进行使用可以不用看后续的使用说明。
昨晚看到评论,发现确实把这个功能点给漏了。

所以额外加了这一节,用来说明如何使用带进度的导入。
还是需要导入maven最新版本:
1 2 3 4 5
| <dependency> <groupId>cn.allbs</groupId> <artifactId>allbs-excel</artifactId> <version>3.1.0</version> </dependency>
|
普通带进度的导入
导入的示例代码:
当前示例设置了两种模式,一种是检测到错误不让导入,一种是有错误跳过。通过设置参数skipErrors实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| @PostMapping("/with-progress") @ImportProgress(listener = SseImportProgressListener.class, interval = 100) public ResponseEntity<?> importWithProgress( @ImportExcel List<UserDTO> users, @RequestAttribute(name = "excelErrors", required = false) List<ErrorMessage> excelErrors, @RequestParam(value = "skipErrors", defaultValue = "false") boolean skipErrors ) { Map<String, Object> result = new HashMap<>(); int totalRows = users.size() + (excelErrors != null ? excelErrors.size() : 0); int successCount = users.size(); int errorCount = excelErrors != null ? excelErrors.size() : 0; if (!CollectionUtils.isEmpty(excelErrors)) { if (skipErrors) { result.put("success", true); result.put("message", String.format("导入完成(跳过了 %d 行错误数据)", errorCount)); result.put("successCount", successCount); result.put("errorCount", errorCount); result.put("totalRows", totalRows); result.put("skipRate", String.format("%.2f%%", (errorCount * 100.0 / totalRows))); List<String> errorSummary = excelErrors.stream() .limit(10) .flatMap(em -> em.getErrorMessages().stream() .map(msg -> "行号 " + em.getLineNum() + ":" + msg)) .collect(Collectors.toList()); if (errorCount > 10) { errorSummary.add("... 还有 " + (errorCount - 10) + " 行错误数据被跳过"); } result.put("errorSummary", errorSummary); } else { List<String> errors = excelErrors.stream() .limit(20) .flatMap(em -> em.getErrorMessages().stream() .map(msg -> "行号 " + em.getLineNum() + ":" + msg)) .collect(Collectors.toList()); if (errorCount > 20) { errors.add("... 还有 " + (errorCount - 20) + " 行错误"); } result.put("success", false); result.put("message", String.format("发现 %d 行数据有错误,请修正后重新导入", errorCount)); result.put("errors", errors); result.put("validCount", successCount); result.put("errorCount", errorCount); return ResponseEntity.badRequest().body(result); } } else { result.put("success", true); result.put("message", "导入成功"); result.put("successCount", successCount); result.put("errorCount", 0); result.put("totalRows", totalRows); } result.put("previewData", users.size() > 10 ? users.subList(0, 10) : users); result.put("hasMore", users.size() > 10); return ResponseEntity.ok(result); }
|
我将使用有一行故意不满足校验的模板:

导入后如下:

带进度的如下:

异步导入
可以用几十万条数据进行测试,不然效果不明显。

接口实时返回内容,然后通过另外一个接口获取这个异步任务的动态。代码分为导入和获取状态的接口。异步处理的我就不贴了,可以看示例代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
|
@PostMapping("/submit") public ResponseEntity<?> submitTask( @RequestParam("file") MultipartFile file, @RequestParam(value = "skipErrors", defaultValue = "false") boolean skipErrors ) { try { String taskId = asyncImportService.submitTask(file, UserDTO.class, skipErrors); Map<String, Object> response = new HashMap<>(); response.put("success", true); response.put("taskId", taskId); response.put("message", "任务已提交,请通过taskId查询进度"); return ResponseEntity.ok(response); } catch (Exception e) { Map<String, Object> response = new HashMap<>(); response.put("success", false); response.put("message", "提交任务失败: " + e.getMessage()); return ResponseEntity.badRequest().body(response); }}
@GetMapping("/task/{taskId}") public ResponseEntity<?> getTaskStatus(@PathVariable String taskId) { ImportTask task = asyncImportService.getTask(taskId); if (task == null) { Map<String, Object> response = new HashMap<>(); response.put("success", false); response.put("message", "任务不存在"); return ResponseEntity.badRequest().body(response); } Map<String, Object> response = new HashMap<>(); response.put("success", true); response.put("taskId", task.getTaskId()); response.put("fileName", task.getFileName()); response.put("status", task.getStatus().name()); response.put("progress", task.getProgress()); response.put("totalRows", task.getTotalRows()); response.put("processedRows", task.getProcessedRows()); response.put("successCount", task.getSuccessCount()); response.put("errorCount", task.getErrorCount()); response.put("errorMessage", task.getErrorMessage()); response.put("createdAt", task.getCreatedAt()); response.put("startedAt", task.getStartedAt()); response.put("completedAt", task.getCompletedAt()); if (task.getErrorList() != null && !task.getErrorList().isEmpty()) { response.put("errors", task.getErrorList().stream() .limit(20) .map(error -> { Map<String, Object> errorMap = new HashMap<>(); errorMap.put("rowIndex", error.getRowIndex()); errorMap.put("data", error.getData()); errorMap.put("fieldErrors", error.getFieldErrors()); return errorMap; }) .collect(Collectors.toList())); response.put("hasMoreErrors", task.getErrorList().size() > 20); } if (task.getSuccessData() != null && !task.getSuccessData().isEmpty()) { response.put("previewData", task.getSuccessData()); response.put("hasMoreData", task.getSuccessCount() > 20); } return ResponseEntity.ok(response); }
|
数据预览和确认导入
类似海康系统里面的那个导入,先将待导入的数据列出来,然后判断是否导入。代码太多了,就不贴了,可以直接看示例。


