# 通用 Excel 模块 `pjcloud-common-excel` 提供 Excel 导入、普通导出以及大数据量 XLSX 分批流式导出能力。 ## 分批流式导出 `BatchExcelExportTemplate` 适用于单次导出不能把全部查询结果和全部工作表行同时保留在 JVM 堆中的场景。它统一负责: - 根据预统计数量循环分页查询; - 使用 `SXSSFWorkbook` 把超出内存窗口的工作表行刷新到磁盘; - 校验分页实际数量与预统计数量一致; - 校验 Excel 2007 单工作表行数上限; - 释放模板流、工作簿、SXSSF 临时 XML 和最终 XLSX 临时文件; - 通过回调接入业务进度、汇总行和文件上传逻辑。 业务模块先增加公共 Excel 依赖: ```xml com.bsd pjcloud-common-excel ``` 典型调用方式: ```java String fileUrl = BatchExcelExportTemplate.builder(total) .batchSize(1000) .dataStartRowIndex(2) .reservedTailRowCount(1) .temporaryFilePrefix("user-export-") .templateSupplier(() -> ResourceUtil.getStream("file/user.xlsx")) .templateInitializer(workbook -> prepareTemplate(workbook)) .pageFetcher((pageNumber, batchSize) -> { Page page = new Page<>(pageNumber, batchSize, false); return userService.pageForExport(page, query).getRecords(); }) .rowWriter((sheet, rowIndex, dataIndex, item, context) -> writeRow(sheet, rowIndex, dataIndex, item, context)) .footerWriter((sheet, rowIndex, processedTotal, context) -> writeSummary(sheet, rowIndex, context)) .progressListener((processedTotal, expectedTotal) -> reporter.advance("已整理 " + processedTotal + " / " + expectedTotal + " 条")) .fileConsumer(path -> upload(Files.readAllBytes(path), fileName)) .execute(); ``` ## 与异步任务组合 公共 Excel 模块不依赖 `pjcloud-common-async-task`。业务层通过 `progressListener` 把每批完成事件转给 `AsyncTaskProgressReporter`,通过 `fileConsumer` 上传完整文件并返回 `AsyncTaskResult` 所需的地址。 异步任务总步数可统一计算: ```java int progressTotal = BatchExcelExportTemplate.calculateProgressTotal(total, batchSize, 2); reporter.updateTotal(progressTotal, "已查询 " + total + " 条数据,将分批生成文件"); ``` 额外的 `2` 由业务流程定义,例如文件上传一步和异步任务成功收尾一步。公共模板每完成一个查询批次只通知一次, 不会按明细行高频写 Redis。 ## 调用约束 - 预统计和分页查询必须使用完全相同的筛选条件、租户上下文和数据权限。 - 分页 SQL 必须有稳定排序;排序字段存在重复值时,需要追加唯一字段,避免翻页时漏行或重复。 - 导出期间数据发生增删会触发数量不一致异常,业务方应提示用户重新导出。 - `dataStartRowIndex` 和 `reservedTailRowCount` 都按 Excel 零基行号语义配置。 - `fileConsumer` 收到的路径只在回调期间有效,不得交给其他线程延迟读取。 - 默认关闭共享字符串表以控制内存;只有确认重复文本占比高且内存可控时才启用。 - 单个工作表仍受 XLSX 的 1,048,576 行上限约束;超过上限时应按业务条件拆分文件或扩展为多工作表导出。