使用easyexcel 和poi 实现模板导出excel 模板只有一个sheet sheet中有一个表格 要求 在一个sheet中 根据list数据生成多个相同格式的表格
时间: 2025-06-26 13:29:13 浏览: 12
<think>嗯,用户想用EasyExcel和POI来实现模板导出Excel,要求在一个sheet里根据list数据生成多个相同格式的表格。首先我得想清楚两种库的处理方式有什么不同,然后怎么实现多个表格的生成。
首先,EasyExcel是基于事件模型的,处理大数据量比较高效,而POI是传统的DOM方式,处理起来可能更灵活但内存消耗大。用户的需求是同一个sheet中有多个结构相同的表格,每个表格对应list中的一个数据项。那怎么动态地插入多个表格呢?
对于EasyExcel来说,通常用模板导出,通过占位符替换数据。但是模板里只能预定义一些区域,比如单个表格的位置。如果要生成多个表格,可能需要用循环指令或者动态调整写入位置。EasyExcel支持在模板中使用`{{}}`的循环语法,比如在模板里用{{#list}}来循环生成行,但这里用户需要的是多个独立的表格,而不是简单的行循环。可能需要用`{{$fe:}}`这样的表达式来控制多个区域的循环,或者通过多次写入数据到不同的起始行。
然后POI的话,因为是直接操作Excel的API,可以在代码里动态创建多个表格结构。比如读取模板,复制样式,然后在不同的行位置插入新的表格,填充数据。POI需要处理行和单元格的样式复制,可能比较繁琐,但灵活性更高。需要注意合并单元格的情况,以及每个表格的位置计算,比如每个表格占多少行,然后下一个表格的起始行是当前行数加间隔。
用户可能遇到的问题包括如何动态确定每个表格的位置,如何保持样式一致,以及处理可能的合并单元格。比如每个表格可能有标题行,数据行,底部合计行,这样每个表格的高度不固定,需要根据数据量动态计算下一个表格的起始位置。
另外,模板设计方面,EasyExcel可能需要模板中有循环区域的标记,而POI可能需要先有一个样例表格,然后复制这个样例的格式到其他位置。对于EasyExcel,可能需要使用`&`符号来标记循环区域的开始和结束,然后每个循环生成一个表格。或者可能需要用多个模板区域,每个对应一个表格,但数据是列表的话,可能需要将整个列表拆分成多个循环块。
比如在EasyExcel模板中,设计一个表格的结构,然后用`{{$fe:dataList}}`来循环生成多个表格,每个表格对应列表中的一个元素。这可能需要模板中的表格行用循环指令包裹,并设置正确的上下文变量。比如每个表格的数据项可能是一个对象,包含表格需要的各个字段,然后模板里通过{{变量名}}来填充。
对于POI,可能需要先读取模板中的第一个表格,获取其样式和结构,然后遍历数据列表,为每个数据项复制这个表格到新的行位置。需要注意调整行高、列宽,以及公式的引用是否正确。例如,使用XSSFWorkbook读取模板,获取第一个sheet,然后找到第一个表格的位置,复制其行和单元格的样式,填充数据,然后移动后续内容,再插入下一个表格。
可能需要考虑性能问题,如果数据量很大,POI可能会内存溢出,这时候建议使用SXSSFWorkbook。而EasyExcel本身处理大数据量较好,但模板导出时循环生成多个表格可能需要合理设计模板结构。
用户可能需要示例代码来具体实现这两种方式。比如,在EasyExcel中使用动态模板,通过List<Object>数据,每个对象对应一个表格的数据,模板中使用循环指令生成多个表格。而在POI中,通过复制行和单元格,调整每个表格的起始行,填充数据。
另外,需要注意Excel的表格(Table)结构,比如使用POI的XSSFTable创建结构化表格,但可能更复杂。用户可能只是需要视觉上的表格,而不是Excel的表格对象,所以可能只需要样式上的复制。
总结下来,两种方式的大致步骤:
EasyExcel:
1. 创建模板,定义单个表格的结构,使用循环指令包裹整个表格区域。
2. 在Java代码中,使用动态参数,将数据列表传递给模板,每个数据项生成一个表格。
3. 可能需要设置每个表格的起始行,通过模板引擎的循环功能自动处理。
POI:
1. 加载模板文件,获取第一个sheet和第一个表格的结构和样式。
2. 遍历数据列表,对每个数据项,复制模板表格的样式到新的行区域。
3. 计算每个表格需要占用的行数,动态调整后续内容的位置。
4. 填充数据,处理合并单元格,保存文件。
需要注意的细节包括样式的深度复制,行高的调整,合并区域的正确设置,以及公式或引用是否正确更新。例如,每个表格可能有不同的合并区域,需要在复制时重新计算起始和结束的行列索引。
可能的错误点:在POI中,如果直接复制行,可能会导致样式被覆盖或者没有正确应用,需要使用CellCopyPolicy来复制样式。同时,插入多行后,原有的行会被下移,需要正确计算插入位置。
对于EasyExcel,可能需要使用自定义的WriteHandler来动态调整写入的位置,或者在模板中使用特定的指令来控制循环区域,确保每个表格的数据正确填充到指定位置。
最后,给出示例代码的大纲,比如EasyExcel如何编写模板和填充数据,POI如何复制行和填充数据,并处理多个表格的位置。</think>使用EasyExcel和Apache POI实现“单Sheet生成多个相同格式表格”可通过不同方式实现。以下是分步解决方案:
一、EasyExcel实现方案(推荐)
$$优点:内存消耗低、适合大数据量、代码简洁$$
1. 模板准备
```excel
${excel|模板位置!A1:D5}
表格标题1
姓名 年龄 部门 薪资
${dataList.name} ${dataList.age} ${dataList.department} ${dataList.salary}
```
2. Java核心代码
```java
// 定义数据对象
@Data
public class TableData {
private String name;
private Integer age;
private String department;
private Double salary;
}
// 导出逻辑
List<TableData> dataList = ...; // 数据集合
String template = "template.xlsx";
String output = "result.xlsx";
ExcelWriter excelWriter = EasyExcel.write(output)
.withTemplate(template)
.build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.VERTICAL).build();
// 关键:使用横向填充生成多个表格
excelWriter.fill(new FillWrapper("dataList", dataList), fillConfig, writeSheet);
excelWriter.finish();
```
二、Apache POI实现方案
$$优点:精细控制、适合复杂格式$$
```java
public void exportWithPOI() throws IOException {
// 1. 加载模板
Workbook workbook = new XSSFWorkbook(new FileInputStream("template.xlsx"));
Sheet sheet = workbook.getSheetAt(0);
// 2. 获取模板表格参数
int startRow = 0; // 模板起始行
int rowsPerTable = 5; // 每个表格占5行
int cols = 4; // 列数
// 3. 遍历数据生成表格
List<TableData> dataList = getData();
for (int i = 0; i < dataList.size(); i++) {
TableData data = dataList.get(i);
// 计算插入位置
int currentStartRow = startRow + (i * (rowsPerTable + 2)); // +2为表格间隔
// 复制模板样式
copyTableStyle(sheet, currentStartRow, rowsPerTable, cols);
// 填充数据
Row dataRow = sheet.getRow(currentStartRow + 2); // 数据行在模板第3行
dataRow.getCell(0).setCellValue(data.getName());
dataRow.getCell(1).setCellValue(data.getAge());
dataRow.getCell(2).setCellValue(data.getDepartment());
dataRow.getCell(3).setCellValue(data.getSalary());
}
// 4. 输出文件
FileOutputStream fos = new FileOutputStream("result_poi.xlsx");
workbook.write(fos);
workbook.close();
}
// 样式复制方法
private void copyTableStyle(Sheet sheet, int startRow, int rowCount, int colCount) {
CellCopyPolicy policy = new CellCopyPolicy();
for (int i = 0; i < rowCount; i++) {
Row sourceRow = sheet.getRow(i); // 原模板行
Row newRow = sheet.createRow(startRow + i);
for (int j = 0; j < colCount; j++) {
Cell sourceCell = sourceRow.getCell(j);
Cell newCell = newRow.createCell(j);
newCell.copyCellFrom(sourceCell, policy);
}
// 处理合并区域
sheet.getMergedRegions().forEach(region -> {
CellRangeAddress newRegion = new CellRangeAddress(
startRow + region.getFirstRow(),
startRow + region.getLastRow(),
region.getFirstColumn(),
region.getLastColumn()
);
sheet.addMergedRegion(newRegion);
});
}
}
```
三、关键差异对比表
| 特性 | EasyExcel | Apache POI |
|--------------------|----------------------------------|----------------------------|
| 内存消耗 | 基于事件模型(~10MB级) | DOM模型(~100MB级) |
| 开发复杂度 | 低(模板驱动) | 高(需手动处理样式) |
| 性能表现 | 适合10万+数据 | 适合1万以内数据 |
| 格式控制精度 | 中等 | 高 |
| 学习曲线 | 平缓 | 陡峭 |
四、注意事项
1. 行号计算:每个表格需要预留足够的行空间,推荐公式:
$$总行数 = 模板行数 \times 数据量 + 间隔行数 \times (数据量-1)$$
2. 样式继承:POI需使用`CellUtil.cloneStyleFrom()`方法深度复制样式
3. 性能优化:POI方案中建议添加内存控制
```java
if(dataList.size() > 5000) {
throw new RuntimeException("建议使用EasyExcel处理大数据量");
}
```
4. 动态标题:可在循环中添加标题处理逻辑
```java
sheet.getRow(startRow).getCell(0)
.setCellValue("部门数据表 - " + data.getDepartment());
```
根据实际需求选择方案:需要处理大数据或快速开发时使用EasyExcel,需要精细控制样式或处理复杂合并时选择POI。
阅读全文
相关推荐


















