填充
本章节介绍如何使用 Fesod 来填充数据到文件中。
占位符语法
模板通过 {} 占位符标记需要填充的单元格,大括号里的内容决定了填充方式:
| 占位符 | 含义 | 由谁填充 |
|---|---|---|
{name} | 单个变量 | doFill(object)、doFill(map) |
{.name} | 列表中每一项的 name 属性 | doFill(list)、fill(list, ...) |
{data1.name} | 同上,但取自名为 data1 的列表 | fill(new FillWrapper("data1", list), ...) |
\{name\} | 用 \ 转义,不会被解析 | 不填充 |
开头的 . 决定了单元格会按列表的每一项重复:默认向下重复,也可以通过 FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL) 向右重复。. 之前的文字指明数据来自哪个列表,因此一个模板中可以并存多个列表。
一个单元格里可以混合多个占位符和普通文本,例如 {name}今年{number}岁了。填充时没有提供的占位符会被清空,而不是原样留在表格里:用列表填充 {name},或者用单个对象填充 {.name},都会清空该单元格,只保留周围的文本。
转义只是让大括号不被解析,而 \ 本身只有在同一个单元格里还存在真实占位符时才会被去掉。如果单元格里没有别的内容,\{name\} 会原样写出,包括反斜杠。
简单填充
概述
基于模板文件,通过对象或 Map 填充数据到电子表格中。
POJO 类
@Getter
@Setter
@EqualsAndHashCode
public class FillData {
private String name;
private double number;
private Date date;
}
数据列表
private List<FillData> data() {
List<FillData> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) {
FillData fillData = new FillData();
fillData.setName("张三" + i);
fillData.setNumber(5.2);
fillData.setDate(new Date());
list.add(fillData);
}
return list;
}
代码示例
@Test
public void simpleFill() {
String templateFileName = "path/to/simple.xlsx";
// 方案1:基于对象填充
FillData fillData = new FillData();
fillData.setName("张三");
fillData.setNumber(5.2);
FesodSheet.write("simpleFill.xlsx")
.withTemplate(templateFileName)
.sheet()
.doFill(fillData);
// 方案2:基于 Map 填充
Map<String, Object> map = new HashMap<>();
map.put("name", "张三");
map.put("number", 5.2);
FesodSheet.write("simpleFillMap.xlsx")
.withTemplate(templateFileName)
.sheet()
.doFill(map);
}
模板
| A | B | C | D | E | |
| 1 | 姓名 | 数字 | 复杂 | 忽略 | 空 |
| 2 | {name} | {number} | {name}今年{number}岁了 | \{name\}忽略,{name} | 空{.empty} |
结果
| A | B | C | D | E | |
| 1 | 姓名 | 数字 | 复杂 | 忽略 | 空 |
| 2 | 张三 | 5.2 | 张三今年5.2岁了 | {name}忽略,张三 | 空 |
填充列表
概述
填充多个数据项到模板列表中,支持内存批量操作和文件缓存分批填充。
代码示例
@Test
public void listFill() {
String templateFileName = "path/to/list.xlsx";
// 方案1:一次性填充所有数据
FesodSheet.write("listFill.xlsx")
.withTemplate(templateFileName)
.sheet()
.doFill(data());
// 方案2:分批填充
try (ExcelWriter writer = FesodSheet.write("listFillBatch.xlsx").withTemplate(templateFileName).build()) {
WriteSheet writeSheet = FesodSheet.writerSheet().build();
writer.fill(data(), writeSheet);
writer.fill(data(), writeSheet);
}
}
模板
| A | B | C | |
| 1 | 姓名 | 数字 | 日期 |
| 2 | {.name} | {.number} | {.date} |
结果
方案1:
| A | B | C | |
| 1 | 姓名 | 数字 | 日期 |
| 2 | 张三0 | 5.2 | 2026-07-31 19:55:44 |
| 3 | 张三1 | 5.2 | 2026-07-31 19:55:44 |
| 4 | 张三2 | 5.2 | 2026-07-31 19:55:44 |
| ⋮ | … | … | … |
| 11 | 张三9 | 5.2 | 2026-07-31 19:55:44 |
方案2:
| A | B | C | |
| 1 | 姓名 | 数字 | 日期 |
| 2 | 张三0 | 5.2 | 2026-07-31 19:55:44 |
| ⋮ | … | … | … |
| 11 | 张三9 | 5.2 | 2026-07-31 19:55:44 |
| 12 | 张三0 | 5.2 | 2026-07-31 19:55:44 |
| ⋮ | … | … | … |
| 21 | 张三9 | 5.2 | 2026-07-31 19:55:44 |
复杂填充
概述
在模板中填充多种数据类型,包括列表和普通变量。
代码示例
@Test
public void complexFill() {
String templateFileName = "path/to/complex.xlsx";
try (ExcelWriter writer = FesodSheet.write("complexFill.xlsx").withTemplate(templateFileName).build()) {
WriteSheet writeSheet = FesodSheet.writerSheet().build();
// 填充列表数据,开启 forceNewRow
FillConfig config = FillConfig.builder().forceNewRow(true).build();
writer.fill(data(), config, writeSheet);
// 填充普通变量
Map<String, Object> map = new HashMap<>();
map.put("date", "2024年11月20日");
map.put("total", 1000);
writer.fill(map, writeSheet);
}
}
模板
| A | B | C | D | |
| 1 | 统计 | |||
| 2 | 时间:{date} | |||
| 3 | 姓名 | 数字 | 姓名 | 数字 |
| 4 | {.name} | {.number} | {.name} | {.number} |
| 5 | 统计:{total} |
结果
| A | B | C | D | |
| 1 | 统计 | |||
| 2 | 时间:2024年11月20日 | |||
| 3 | 姓名 | 数字 | 姓名 | 数字 |
| 4 | 张三0 | 5.2 | 张三0 | 5.2 |
| 5 | 张三1 | 5.2 | 张三1 | 5.2 |
| 6 | 张三2 | 5.2 | 张三2 | 5.2 |
| ⋮ | … | … | … | … |
| 13 | 张三9 | 5.2 | 张三9 | 5.2 |
| 14 | 统计:1000 |
大数据量填充
概述
优化大数据量填充性能,确保模板列表在最后一行,后续数据通过 WriteTable 填充。
代码示例
@Test
public void complexFillWithTable() {
String templateFileName = "path/to/complexFillWithTable.xlsx";
try (ExcelWriter writer = FesodSheet.write("complexFillWithTable.xlsx").withTemplate(templateFileName).build()) {
WriteSheet writeSheet = FesodSheet.writerSheet().build();
// 填充列表数据
writer.fill(data(), writeSheet);
// 填充其他变量
Map<String, Object> map = new HashMap<>();
map.put("date", "2024年11月20日");
writer.fill(map, writeSheet);
// 填充统计信息
List<List<String>> totalList = new ArrayList<>();
totalList.add(Arrays.asList(null, null, null, "统计: 1000"));
writer.write(totalList, writeSheet);
}
}
模板
| A | B | C | D | |
| 1 | 统计 | |||
| 2 | 时间:{date} | |||
| 3 | 姓名 | 数字 | 姓名 | 数字 |
| 4 | {.name} | {.number} | {.name} | {.number} |
结果
最终文件与上面的复杂填充相同,区别在于生成方式:模板到列表行为止,不再为 {total} 预留一行,统计信息在填充之后通过 writer.write(...) 追加,因此列表可以任意增长,下方没有需要被顶下去的行。
| A | B | C | D | |
| 1 | 统计 | |||
| 2 | 时间:2024年11月20日 | |||
| 3 | 姓名 | 数字 | 姓名 | 数字 |
| 4 | 张三0 | 5.2 | 张三0 | 5.2 |
| 5 | 张三1 | 5.2 | 张三1 | 5.2 |
| 6 | 张三2 | 5.2 | 张三2 | 5.2 |
| ⋮ | … | … | … | … |
| 13 | 张三9 | 5.2 | 张三9 | 5.2 |
| 14 | 统计: 1000 |
横向填充
概述
将列表数据横向填充,适用于动态列数场景。
代码示例
@Test
public void horizontalFill() {
String templateFileName = "path/to/horizontal.xlsx";
try (ExcelWriter writer = FesodSheet.write("horizontalFill.xlsx").withTemplate(templateFileName).build()) {
WriteSheet writeSheet = FesodSheet.writerSheet().build();
FillConfig config = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
writer.fill(data(), config, writeSheet);
Map<String, Object> map = new HashMap<>();
map.put("date", "2024年11月20日");
writer.fill(map, writeSheet);
}
}
模板
| A | B | C | |
| 1 | 统计 | 姓名 | {.name} |
| 2 | 数字 | {.number} | |
| 3 | 姓名 | {.name} | |
| 4 | 数字 | {.number} | |
| 5 | 时间:{date} |
结果
| A | B | C | D | E | ⋯ | L | |
| 1 | 统计 | 姓名 | 张三0 | 张三1 | 张三2 | … | 张三9 |
| 2 | 数字 | 5.2 | 5.2 | 5.2 | … | 5.2 | |
| 3 | 姓名 | 张三0 | 张三1 | 张三2 | … | 张三9 | |
| 4 | 数字 | 5.2 | 5.2 | 5.2 | … | 5.2 | |
| 5 | 时间:2024年11月20日 |
多列表组合填充
概述
支持多个列表同时填充,列表之间通过前缀区分。
代码示例
@Test
public void compositeFill() {
String templateFileName = "path/to/composite.xlsx";
try (ExcelWriter writer = FesodSheet.write("compositeFill.xlsx").withTemplate(templateFileName).build()) {
WriteSheet writeSheet = FesodSheet.writerSheet().build();
// 使用 FillWrapper 进行多列表填充
// data1 在模板中是横向排列的,因此使用横向填充
FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
writer.fill(new FillWrapper("data1", data()), fillConfig, writeSheet);
writer.fill(new FillWrapper("data2", data()), writeSheet);
writer.fill(new FillWrapper("data3", data()), writeSheet);
Map<String, Object> map = new HashMap<>();
map.put("date", new Date());
writer.fill(map, writeSheet);
}
}
模板
| A | B | C | D | E | |
| 1 | 统计 | 姓名 | {data1.name} | ||
| 2 | 数字 | {data1.number} | |||
| 3 | 姓名 | {data1.name} | |||
| 4 | 数字 | {data1.number} | |||
| 5 | 时间:{date} | ||||
| 6 | |||||
| 7 | |||||
| 8 | 姓名 | 数字 | |||
| 9 | {data2.name} | {data2.number} | |||
| 10 | 姓名 | 数字 | |||
| 11 | {data3.name} | {data3.number} |
结果
data1采用横向填充,十条数据在四个模板行上从C列排到L列。data2和data3则是纵向填充,分别占据第 9 到 18 行的A/B列和第 11 到 20 行的D/E列。- 用同一个列表名调用
fill会接着往后追加,参见填充列表。
| A | B | C | D | E | ⋯ | L | |
| 1 | 统计 | 姓名 | 张三0 | 张三1 | 张三2 | … | 张三9 |
| 2 | 数字 | 5.2 | 5.2 | 5.2 | … | 5.2 | |
| 3 | 姓名 | 张三0 | 张三1 | 张三2 | … | 张三9 | |
| 4 | 数字 | 5.2 | 5.2 | 5.2 | … | 5.2 | |
| 5 | 时间:2026-07-31 20:04:59 | ||||||
| 6 | |||||||
| 7 | |||||||
| 8 | 姓名 | 数字 | |||||
| 9 | 张三0 | 5.2 | |||||
| 10 | 张三1 | 5.2 | 姓名 | 数字 | |||
| 11 | 张三2 | 5.2 | 张三0 | 5.2 | |||
| 12 | 张三3 | 5.2 | 张三1 | 5.2 | |||
| ⋮ | … | … | … | … | |||
| 18 | 张三9 | 5.2 | 张三7 | 5.2 | |||
| 19 | 张三8 | 5.2 | |||||
| 20 | 张三9 | 5.2 |