Skip to main content

easyexcel-fill

基于 Alibaba EasyExcel(com.alibaba:easyexcel:4.0.3)的 Excel 模板填充技能。覆盖简单对象填充、列表填充({.}+forceNewRow)、横向多列组合填充(FillWrapper+WriteDirectionEnum.HORIZONTAL)、复杂填充(含合计行/统计行/先 fill 后 write)、Spring Boot 集成、模板设计规范、转义字符、03版/07版差异、性能优化(分次 fill、文件缓存)。当用户需要把 Java 对象或 Map 数据按预制 .xlsx 模板(变量语法 {var}、{.}、{prefix.})填充生成报表(工资条/合同/对账单/发票/财务报表/统计表/汇总表)时使用此技能,不适用于不基于模板的"程序生成 Excel"场景(改用 easyexcel-write)。

インストールへ移動

ソース情報

リポジトリ
full-stack-skills/java-skills
ソースの最終更新活動
2026年7月13日 08:00
検出された SKILL.md の言語
中国語
スター
5
フォーク
3

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
35 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
easyexcel-fill
description
基于 Alibaba EasyExcel(com.alibaba:easyexcel:4.0.3)的 Excel 模板填充技能。覆盖简单对象填充、列表填充({.}+forceNewRow)、横向多列组合填充(FillWrapper+WriteDirectionEnum.HORIZONTAL)、复杂填充(含合计行/统计行/先 fill 后 write)、Spring Boot 集成、模板设计规范、转义字符、03版/07版差异、性能优化(分次 fill、文件缓存)。当用户需要把 Java 对象或 Map 数据按预制 .xlsx 模板(变量语法 {var}、{.}、{prefix.})填充生成报表(工资条/合同/对账单/发票/财务报表/统计表/汇总表)时使用此技能,不适用于不基于模板的"程序生成 Excel"场景(改用 easyexcel-write)。
license
Apache-2.0
# EasyExcel 模板填充(Fill) > 官方文档:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/fill > 官方示例:https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java > 当前版本:`com.alibaba:easyexcel:4.0.3`(Apache-2.0,仓库已归档,仅做 Bug 修复) 本技能专注于 **"按预制模板填充"** 场景。当用户给出一份已经画好样式的 `.xlsx` 模板,要求把数据库/接口里的数据填进去(工资条、合同、发票、报表、统计单等),应当使用本技能。 ## Capability Boundaries ### ✅ 强项 1. 简单对象填充(Map / 单个 DTO) 2. 列表填充(模板 `{.}` 语法,单向纵向) 3. 复杂填充(list 后还有内容 → `forceNewRow=true`) 4. 大数据量复杂填充(list 必须放最后一行,后续统计行用 `write` 追加) 5. 横向多列组合填充(`WriteDirectionEnum.HORIZONTAL` + `FillWrapper`) 6. 模板转义字符处理(`\{` `\}`) 7. Spring Boot 集成(模板文件可来自 resources/ 或 OSS) ### ⚠️ 限制 1. 仅支持 `.xlsx`(07 版),03 版(`.xls`)不支持复杂/大数据量填充 2. `forceNewRow=true` 会全量驻内存,数据量大时慎用 3. `withTemplate` 模板会全量加载到内存,模板文件过大(>几十 MB)会 OOM 4. Map 填充 list 时必须包含所有 list 的 key,否则报 NPE ### ❌ Out of Scope(不该用本技能的场景,请改用其它技能) 1. **不基于模板的程序化生成 Excel**(无预制模板)→ **不适用**本技能,使用 `easyexcel-write` 2. **读取 Excel 数据** → **不适用**本技能,使用 `easyexcel-read` 3. **CSV 文件处理** → **不适用**本技能,自行使用 commons-csv / OpenCSV 4. **Word/PPT** → **不适用**本技能,使用 Apache POI / Apache POI-XML ## Data Privacy 本技能不收集、存储或传输任何用户数据。所有代码示例仅用于本地开发参考。 ## When to use this skill - 用户说"按模板生成 Excel"、"填充 Excel 模板"、"导出工资条/合同/发票" - 用户提供 `.xlsx` 模板文件,要求把数据写进变量占位符 - 报表存在固定格式,需要保留样式/合并单元格/函数/图表 - 业务上有"先 fill 列表,再追加合计行/统计行"的需求 ## Quick Start **典型调用方式(4 选 1,按文件大小递增)**: ```java // 方案 A:最简单的对象填充(since 2.1.1) String fileName = "output_simple.xlsx"; EasyExcel.write(fileName).withTemplate("templates/simple.xlsx") .sheet().doFill(fillData); // 方案 B:列表填充(一次放入内存) EasyExcel.write(fileName).withTemplate("templates/list.xlsx") .sheet().doFill(list); // 方案 C:列表分次填充(文件缓存,省内存) try (ExcelWriter excelWriter = EasyExcel.write(fileName) .withTemplate("templates/list.xlsx").build()) { WriteSheet writeSheet = EasyExcel.writerSheet().build(); excelWriter.fill(list, writeSheet); // 多次 fill,自动文件缓存 excelWriter.fill(list, writeSheet); } // 方案 D:先 fill 列表 + 后 write 追加合计行(推荐:复杂报表 + 大数据量) try (ExcelWriter excelWriter = EasyExcel.write(fileName) .withTemplate("templates/complex.xlsx").build()) { WriteSheet writeSheet = EasyExcel.writerSheet().build(); excelWriter.fill(list, writeSheet); // 1. 填列表 excelWriter.fill(headerMap, writeSheet); // 2. 填头部变量 excelWriter.write(totalListList, writeSheet);// 3. 手动追加合计行 } ``` ## Workflow Step 1. **确认场景** — 用户是否提供了 `.xlsx` 模板?模板里是否有 `{var}` `{.}` `{prefix.}` 占位符? Step 2. **评估数据量** — < 1 万行:方案 A/B;> 1 万行:方案 C/D(list 必须放最后一行) Step 3. **选择填充模式** — 单对象 / 列表 / 复杂 / 横向多列 Step 4. **构造数据** — Map / DTO / List;Map 填充 list 必须包含所有 key Step 5. **执行填充** — try-with-resources 关闭 `ExcelWriter` Step 6. **验证输出** — 打开生成的 `.xlsx` 检查占位符是否被替换、有无残留 `{.}` `{}` ## Critical: 模板语法速查 | 模板占位符 | 含义 | Java 数据结构 | |------------|------|---------------| | `{name}` `{number}` `{date}` | 单值占位符 | `Map.put("name", value)` 或 DTO 字段 | | `{.}` | 列表占位符(默认纵向) | `List<DTO>` 整体作为 fill 第一个参数 | | `{data1.}` `{data2.}` | 带前缀的列表(多列表区分) | `new FillWrapper("data1", list)` | | `\{` `\}` | 转义:当模板中真有 `{` `}` 字符时 | —— | > **官方原文**:"{} 普通变量,{.} list 的变量。如果本来就有'{'、'}' 特殊字符 用'\\{'、'\\}' 代替。{.前缀可以区分不同的list。" ## Critical: 4 种填充场景完整示例 ### 场景 1:简单对象填充 **DTO**: ```java @Getter @Setter @EqualsAndHashCode public class FillData { private String name; private double number; private Date date; } ``` **模板**(`templates/simple.xlsx`):单元格含 `{name}` `{number}` `{date}` **代码**: ```java @Test public void simpleFill() { String templateFileName = "templates/simple.xlsx"; String fileName = "output_simpleFill_" + System.currentTimeMillis() + ".xlsx"; // 方案 1:根据对象填充 FillData fillData = new FillData(); fillData.setName("张三"); fillData.setNumber(5.2); EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData); // 方案 2:根据 Map 填充 Map<String, Object> map = new HashMap<>(); map.put("name", "张三"); map.put("number", 5.2); EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); } ``` ### 场景 2:列表填充(模板 `{.}` 占位一行) **模板**(`templates/list.xlsx`):一行单元格含 `{.}` 表示 list 起点 **代码**: ```java @Test public void listFill() { String templateFileName = "templates/list.xlsx"; String fileName = "output_listFill_" + System.currentTimeMillis() + ".xlsx"; // 方案 1:一次性放入内存 EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data()); // 方案 2:分次填充(文件缓存,省内存,推荐) try (ExcelWriter excelWriter = EasyExcel.write(fileName) .withTemplate(templateFileName).build()) { WriteSheet writeSheet = EasyExcel.writerSheet().build(); excelWriter.fill(data(), writeSheet); excelWriter.fill(data(), writeSheet); } } ``` > **官方提示**:"分多次 填充 会使用文件缓存" ### 场景 3:复杂填充(list 后还有合计行/总计行) **场景特征**:模板中 `{.}` 列表行下面还有非列表内容(如"总计:xxx")。 ```java @Test public void complexFill() { String templateFileName = "templates/complex.xlsx"; String fileName = "output_complexFill_" + System.currentTimeMillis() + ".xlsx"; try (ExcelWriter excelWriter = EasyExcel.write(fileName) .withTemplate(templateFileName).build()) { WriteSheet writeSheet = EasyExcel.writerSheet().build(); // ⚠️ 关键:forceNewRow=true 才能让 list 后面再追加内容 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); excelWriter.fill(data(), fillConfig, writeSheet); excelWriter.fill(data(), fillConfig, writeSheet); Map<String, Object> map = new HashMap<>(); map.put("date", "2019年10月9日13:28:28"); map.put("total", 1000); excelWriter.fill(map, writeSheet); } } ``` > **官方原文**:"在 02 版上面 模板 list 不是最后一行 下面还有数据 需要设置 `forceNewRow=true` 但是有一个缺点 就是他会把所有的数据都放到内存了 慎用" > **官方原文**:"如果数据量大 list不是最后一行 参照下一个(complexFillWithTable)" ### 场景 4:数据量大的复杂填充(list 放最后一行 + write 追加) **最佳实践模板设计**:删除模板中 list 之后的"合计行"占位行,让 list 占最后一行;后续"统计行"用 `write` 手动追加。 ```java @Test public void complexFillWithTable() { String templateFileName = "templates/complexFillWithTable.xlsx"; String fileName = "output_complexFillWithTable_" + System.currentTimeMillis() + ".xlsx"; try (ExcelWriter excelWriter = EasyExcel.write(fileName) .withTemplate(templateFileName).build()) { WriteSheet writeSheet = EasyExcel.writerSheet().build(); // 1. 写入 list 数据 excelWriter.fill(data(), writeSheet); excelWriter.fill(data(), writeSheet); // 2. 写入 list 之前的变量 Map<String, Object> map = new HashMap<>(); map.put("date", "2019年10月9日13:28:28"); excelWriter.fill(map, writeSheet); // 3. list 后面手动 write 统计行 List<List<String>> totalListList = new ArrayList<>(); List<String> totalList = new ArrayList<>(); totalListList.add(totalList); totalList.add(null); totalList.add(null); totalList.add(null); totalList.add("统计:1000"); excelWriter.write(totalListList, writeSheet); } } ``` > **官方原文**:"这里是write 别和fill 搞错了" ### 场景 5:横向填充(多列横向并列) ```java @Test public void horizontalFill() { String templateFileName = "templates/horizontal.xlsx"; String fileName = "output_horizontalFill_" + System.currentTimeMillis() + ".xlsx"; try (ExcelWriter excelWriter = EasyExcel.write(fileName) .withTemplate(templateFileName).build()) { WriteSheet writeSheet = EasyExcel.writerSheet().build(); // 横向:列方向展开 FillConfig fillConfig = FillConfig.builder() .direction(WriteDirectionEnum.HORIZONTAL).build(); excelWriter.fill(data(), fillConfig, writeSheet); excelWriter.fill(data(), fillConfig, writeSheet); Map<String, Object> map = new HashMap<>(); map.put("date", "2019年10月9日13:28:28"); excelWriter.fill(map, writeSheet); } } ``` ### 场景 6:多列表组合填充(同时填多个 list) **模板**:包含 `{data1.}` `{data2.}` `{data3.}` 三种带前缀的列表 ```java @Test public void compositeFill() { String templateFileName = "templates/composite.xlsx"; String fileName = "output_compositeFill_" + System.currentTimeMillis() + ".xlsx"; try (ExcelWriter excelWriter = EasyExcel.write(fileName) .withTemplate(templateFileName).build()) { WriteSheet writeSheet = EasyExcel.writerSheet().build(); FillConfig fillConfig = FillConfig.builder() .direction(WriteDirectionEnum.HORIZONTAL).build(); // ⚠️ 关键:多个 list 必须用 FillWrapper 包裹,prefix 必须与模板占位符前缀一致 excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); excelWriter.fill(new FillWrapper("data2", data()), writeSheet); // data2 纵向 excelWriter.fill(new FillWrapper("data3", data()), writeSheet); Map<String, Object> map = new HashMap<>(); map.put("date", new Date()); excelWriter.fill(map, writeSheet); } } ``` > **官方原文**:"多个 list 必须用 `FillWrapper` 包裹;`{前缀.}` 前缀可以区分不同的 list;不同 list 可以指定不同方向(HORIZONTAL/VERTICAL)" ## Critical: Spring Boot 集成 ### 1) 模板放在 `resources/templates/` ```java @Service public class SalaryFillService { public void fillSalary(String employeeId, OutputStream out) { InputStream templateIn = getClass() .getResourceAsStream("/templates/salary.xlsx"); SalaryDTO data = salaryMapper.findByEmployee(employeeId); // 模板以流方式加载,避免临时文件 try (ExcelWriter writer = EasyExcel.write(out) .withTemplate(templateIn).build()) { WriteSheet sheet = EasyExcel.writerSheet().build(); writer.fill(data, sheet); } } } ``` ### 2) Controller 返回 ```java @GetMapping("/export/salary/{employeeId}") public void exportSalary(@PathVariable String employeeId, HttpServletResponse response) throws IOException { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("工资条", "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); salaryFillService.fillSalary(employeeId, response.getOutputStream()); } ``` ### 3) 复杂场景:工资条批量打包 ```java public void batchExportSalary(List<String> employeeIds, OutputStream zipOut) { try (ZipOutputStream zos = new ZipOutputStream(zipOut); ExcelWriter writer = EasyExcel.write(/* unused */).build()) { // 实际逐个写入临时文件 for (String id : employeeIds) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); fillSalary(id, baos); ZipEntry entry = new ZipEntry("salary_" + id + ".xlsx"); zos.putNextEntry(entry); zos.write(baos.toByteArray()); zos.closeEntry(); } } }
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る