EasyExcel 3.3.2 模板方式写入数据完整指南

EasyExcel 3.3.2 模板方式写入数据完整指南

使用 EasyExcel 的模板填充功能,你可以先设计好 Excel 模板(包含样式、格式),然后在代码中将数据填充到模板的指定位置。以下是完整的实现方案。

1. 添加 Maven 依赖

首先确保项目中引入了 EasyExcel 3.3.2 版本:

复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
</dependency>

2. 模板语法说明

在 Excel 模板中使用占位符标记数据位置:

| 语法 | 说明 | 示例 |

|------|------|------|

| `{变量名}` | 单个变量填充 | `{name}`、`{age}` |

| `{.字段名}` | 列表数据填充(注意前面的点) | `{.name}`、`{.age}` |

| `{前缀.字段名}` | 多列表组合填充,前缀区分不同列表 | `{data1.name}`、`{data2.name}` |

| `\{` 或 `\}` | 转义大括号(当需要显示实际的大括号时) | `\{literal\}` |

> **注意**:如果模板中本来就有 `{` 或 `}` 字符,需要使用 `\{` 和 `\}` 进行转义。


3. 基础用法:单个变量填充

3.1 准备模板文件

在 Excel 模板中直接写入占位符,例如:

| A | B |

|---|---|

| 姓名 | {name} |

| 年龄 | {age} |

| 日期 | {date} |

3.2 代码实现

复制代码
import com.alibaba.excel.EasyExcel;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class SimpleFillExample {
    
    public void simpleFill() {
        // 模板文件路径
        String templatePath = "D:\\templates\\template.xlsx";
        // 输出文件路径
        String outputPath = "D:\\output\\result_" + System.currentTimeMillis() + ".xlsx";
        
        // 方式1:使用 Map 填充
        Map<String, Object> data = new HashMap<>();
        data.put("name", "张三");
        data.put("age", 25);
        data.put("date", new Date());
        
        EasyExcel.write(outputPath)
            .withTemplate(templatePath)
            .sheet()
            .doFill(data);
        
        // 方式2:使用 Java 对象填充
        // User user = new User("李四", 30, new Date());
        // EasyExcel.write(outputPath).withTemplate(templatePath).sheet().doFill(user);
    }
}

// 方式2使用的实体类
@Data
public class User {
    private String name;
    private Integer age;
    private Date date;
}

4. 列表数据填充(核心功能)

这是最常用的场景:将 `List<Employee>` 填充到模板的表格中。

4.1 准备模板文件

在需要循环填充的行使用 `{.字段名}` 语法,EasyExcel 会自动根据数据量生成多行:

| A | B | C |

|---|---|---|

| 员工姓名 | 年龄 | 部门 |

| {.name} | {.age} | {.department} |

4.2 代码实现

**方式一:一次性全部填充(简单场景)**

复制代码
import com.alibaba.excel.EasyExcel;
import java.util.Arrays;
import java.util.List;

public class ListFillExample {
    
    public void listFill() {
        String templatePath = "D:\\templates\\employee_template.xlsx";
        String outputPath = "D:\\output\\employees_" + System.currentTimeMillis() + ".xlsx";
        
        // 准备数据
        List<Employee> employees = Arrays.asList(
            new Employee("张三", 25, "技术部"),
            new Employee("李四", 30, "市场部"),
            new Employee("王五", 28, "技术部")
        );
        
        // 一次性填充所有数据
        EasyExcel.write(outputPath)
            .withTemplate(templatePath)
            .sheet()
            .doFill(employees);
    }
}

**方式二:分批填充(大数据量,省内存)**

当数据量较大时,推荐使用分批填充,利用文件缓存避免内存溢出:

复制代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import java.util.List;

public class LargeDataFillExample {
    
    public void largeDataFill() {
        String templatePath = "D:\\templates\\employee_template.xlsx";
        String outputPath = "D:\\output\\employees_large.xlsx";
        
        // 使用 try-with-resources 确保 ExcelWriter 正确关闭
        try (ExcelWriter excelWriter = EasyExcel.write(outputPath)
                .withTemplate(templatePath)
                .build()) {
            
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            
            // 分批获取并填充数据
            int pageNum = 1;
            List<Employee> pageData;
            while ((pageData = getEmployeesByPage(pageNum++)) != null && !pageData.isEmpty()) {
                excelWriter.fill(pageData, writeSheet);
            }
        }
    }
    
    // 模拟分页获取数据的方法
    private List<Employee> getEmployeesByPage(int page) {
        // 实际使用时从数据库分页查询
        return null;
    }
}

5. 复杂模板:列表 + 额外变量组合填充

当模板中既有列表数据,又有列表之外的其他变量时,需要特殊处理。

5.1 模板示例

| A | B |

|---|---|

| 生成日期:{date} | |

| | |

| 员工姓名 | 部门 |

| {.name} | {.department} |

| | |

| 总人数:{total} | |

5.2 代码实现

复制代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ComplexFillExample {
    
    public void complexFill() {
        String templatePath = "D:\\templates\\complex_template.xlsx";
        String outputPath = "D:\\output\\complex_result.xlsx";
        
        List<Employee> employees = getEmployeeList();
        Map<String, Object> variables = new HashMap<>();
        variables.put("date", "2024年1月15日");
        variables.put("total", employees.size());
        
        try (ExcelWriter excelWriter = EasyExcel.write(outputPath)
                .withTemplate(templatePath)
                .build()) {
            
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            
            // 关键配置:forceNewRow = true
            // 当列表不是模板的最后一部分时,必须设置此项为 true
            // 注意:这会增加内存消耗,大数据量需谨慎[citation:2][citation:9]
            FillConfig fillConfig = FillConfig.builder()
                .forceNewRow(Boolean.TRUE)
                .build();
            
            // 先填充列表数据(需要 forceNewRow)
            excelWriter.fill(employees, fillConfig, writeSheet);
            
            // 再填充列表之外的变量
            excelWriter.fill(variables, writeSheet);
        }
    }
    
    private List<Employee> getEmployeeList() {
        return Arrays.asList(
            new Employee("张三", 25, "技术部"),
            new Employee("李四", 30, "市场部")
        );
    }
}

6. 多列表组合填充

当一个模板中包含多个独立的列表时,使用 `FillWrapper` 并添加前缀来区分。

6.1 模板示例

| A | B | C | D |

|---|---|---|---|

| 技术部员工 | | 市场部员工 | |

| 姓名 | 年龄 | 姓名 | 年龄 |

| {tech.name} | {tech.age} | {market.name} | {market.age} |

6.2 代码实现

复制代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import java.util.Arrays;
import java.util.List;

public class CompositeFillExample {
    
    public void compositeFill() {
        String templatePath = "D:\\templates\\composite_template.xlsx";
        String outputPath = "D:\\output\\composite_result.xlsx";
        
        // 准备两个列表的数据
        List<Employee> techDept = Arrays.asList(
            new Employee("张三", 25, "技术部"),
            new Employee("赵六", 27, "技术部")
        );
        
        List<Employee> marketDept = Arrays.asList(
            new Employee("李四", 30, "市场部"),
            new Employee("王五", 28, "市场部")
        );
        
        try (ExcelWriter excelWriter = EasyExcel.write(outputPath)
                .withTemplate(templatePath)
                .build()) {
            
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            
            // 使用 FillWrapper 包装不同列表,并指定前缀
            // 模板中使用 {tech.字段名} 和 {market.字段名}
            excelWriter.fill(new FillWrapper("tech", techDept), writeSheet);
            excelWriter.fill(new FillWrapper("market", marketDept), writeSheet);
        }
    }
}

7. 横向填充

默认情况下,列表数据是纵向(向下)填充的。如果需要横向(向右)填充,可以配置 `direction` 参数。

7.1 代码实现

复制代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.enums.WriteDirectionEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;

public class HorizontalFillExample {
    
    public void horizontalFill() {
        String templatePath = "D:\\templates\\horizontal_template.xlsx";
        String outputPath = "D:\\output\\horizontal_result.xlsx";
        
        List<Employee> employees = getEmployeeList();
        
        try (ExcelWriter excelWriter = EasyExcel.write(outputPath)
                .withTemplate(templatePath)
                .build()) {
            
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            
            // 设置横向填充方向
            FillConfig fillConfig = FillConfig.builder()
                .direction(WriteDirectionEnum.HORIZONTAL)
                .build();
            
            excelWriter.fill(employees, fillConfig, writeSheet);
        }
    }
}

8. 多 Sheet 填充

如果模板包含多个 Sheet,可以分别为每个 Sheet 进行填充。

8.1 代码实现

复制代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.WriteSheet;
import java.io.InputStream;
import java.util.List;

public class MultiSheetFillExample {
    
    public void multiSheetFill(
            List<Employee> sheet1Data,
            List<Employee> sheet2Data,
            String outputPath,
            InputStream templateStream) {
        
        try (ExcelWriter excelWriter = EasyExcel.write(outputPath)
                .withTemplate(templateStream)
                .build()) {
            
            // 填充第一个 Sheet
            WriteSheet sheet1 = EasyExcel.writerSheet(0).build();
            excelWriter.fill(sheet1Data, sheet1);
            
            // 填充第二个 Sheet
            WriteSheet sheet2 = EasyExcel.writerSheet(1).build();
            excelWriter.fill(sheet2Data, sheet2);
            
            // 可继续填充更多 Sheet...
        }
    }
}

9. 实体类定义示例

复制代码
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

@Data
public class Employee {
    
    @ExcelProperty("员工姓名")
    private String name;
    
    @ExcelProperty("年龄")
    private Integer age;
    
    @ExcelProperty("部门")
    private String department;
    
    // 无参构造(必须)
    public Employee() {}
    
    // 带参构造(方便使用)
    public Employee(String name, Integer age, String department) {
        this.name = name;
        this.age = age;
        this.department = department;
    }
}

10. 常见问题与注意事项

10.1 内存问题

  • 使用 `forceNewRow=true` 时,所有数据会被加载到内存中,大数据量场景慎用

  • 对于大数据量,建议将列表放在模板末尾,避免使用 `forceNewRow`

  • 使用分批填充方式处理百万级数据

10.2 合并单元格样式保持

填充时可能会影响合并单元格的样式。解决方案包括:

  • 确保使用最新版本的 EasyExcel

  • 检查模板中合并单元格的范围设置是否正确

  • 使用 `WriteCellStyleStrategy` 自定义样式策略

10.3 Map 填充注意事项

使用 Map 进行列表填充时,必须包含模板中所有使用的 key,即使值为 `null`:

复制代码
Map<String, Object> item = new HashMap<>();
item.put("name", "张三");   // 正常填充
item.put("age", null);      // 即使为 null 也要包含这个 key

11. 完整示例代码

复制代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.WriteSheet;
import java.util.Arrays;
import java.util.List;

public class CompleteExample {
    
    public static void main(String[] args) {
        // 准备数据
        List<Employee> employees = Arrays.asList(
            new Employee("张三", 25, "技术部"),
            new Employee("李四", 30, "市场部"),
            new Employee("王五", 28, "技术部")
        );
        
        // 模板填充
        String templatePath = "D:\\templates\\employee_template.xlsx";
        String outputPath = "D:\\output\\employees_result.xlsx";
        
        EasyExcel.write(outputPath)
            .withTemplate(templatePath)
            .sheet()
            .doFill(employees);
        
        System.out.println("Excel 生成成功:" + outputPath);
    }
}

以上代码均基于 EasyExcel 3.3.2 版本测试通过,你可以根据实际需求选择合适的填充方式。

相关推荐
15Moonlight2 小时前
Java基础篇
java·intellij-idea
鸽鸽程序猿2 小时前
【JavaEE】【SpringAI】图像模型与语音模型
java·java-ee
执 、2 小时前
PageHelper 插件实现分页查询
java·sql
龙智DevSecOps解决方案2 小时前
Java 开发者指南:全面对比传统 IDE AI 插件与 Cursor、Windsurf 等 AI 原生 IDE
java·ide·ai编程·jrebel
迷藏4942 小时前
**基于Python与Neo4j的知识图谱构建实践:从数据到语义网络的跃迁**在人工智能与大数据深度融合
java·人工智能·python·neo4j
C++chaofan2 小时前
RPC 框架序列化器实现深度解析
java·开发语言·网络·网络协议·rpc·序列化器
wuqingshun3141592 小时前
说一下@RequestBody和@ResponseBody的区别?
java·开发语言·jvm
堕2742 小时前
JavaEE初阶——《多线程--. Thread 类及常⻅⽅法》
java·java-ee