- 保持样式一致:预先设计好标题、字体、颜色、边框、Logo、合并单元格等复杂样式。
- 简化代码:无需在 Java 代码中硬编码单元格样式、行高、列宽等,代码更简洁。
- 提高灵活性:非开发人员(如产品经理、运营)可以轻松修改模板,而无需改动代码。
下面我将为你提供一个非常详细和实用的 Java Excel 导出模板方案,以目前最流行的 EasyExcel 为例,并包含完整的代码示例。

为什么选择 EasyExcel?
- 阿里巴巴开源:社区活跃,稳定可靠。
- 性能卓越:解决了传统 POI 大数据量导出时内存溢出的问题,采用 SAX 模式读取。
- API 简单:对 POI 进行了深度封装,API 更易于使用。
- 功能强大:支持注解、复杂头、动态头、数据转换、模板导出等丰富功能。
使用 EasyExcel 的模板导出(推荐)
这是最符合“模板”概念的方法,即我们提供一个 .xlsx 文件作为模板,EasyExcel 会读取这个模板,然后将数据填充到指定的位置。
步骤 1:添加 Maven 依赖
在你的 pom.xml 文件中添加 EasyExcel 的依赖。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version> <!-- 建议使用最新版本 -->
</dependency>
步骤 2:准备 Excel 模板文件
这是整个方案的核心,模板文件需要遵循 EasyExcel 的特定规则来标记数据填充的位置。
- 创建一个 Excel 文件,
user_template.xlsx。 - 在需要填充数据的地方,使用 语法来定义占位符。
- 填充集合数据,会自动循环创建行。
- 填充单个对象数据。
{.属性名}:填充对象的具体属性值。
示例模板 user_template.xlsx:

你可以用 Excel 打开它,看起来就像一个普通的报表,但注意 B2, B3, B4 和 B7 单元格的内容。
| A | B | C | |
|---|---|---|---|
| 1 | 用户信息报表 | ||
| 2 | 报表生成时间 | {.reportTime} |
|
| 3 | 制表人 | {.operator} |
|
| 4 | 总用户数 | {.totalUserCount} |
|
| 5 | |||
| 6 | ID | 姓名 | 邮箱 |
| 7 | {{id}} |
{{name}} |
{{email}} |
| 8 | ... | ... | ... |
| 9 | ... | ... | ... |
模板说明:
- B2:
{.reportTime}表示填充一个Map或POJO中reportTime属性的值。 - B3:
{.operator}同理,填充operator属性的值。 - B4:
{.totalUserCount}填充totalUserCount属性的值。 - B7, C7, D7:
{{id}},{{name}},{{email}}表示这里将填充一个 List 集合,集合中的每个元素都是一个对象,EasyExcel 会循环读取这个 List,为每个对象创建一行,并将对象的id,name,email属性值填充进去。
步骤 3:编写 Java 代码
我们创建一个 Java 类来填充这个模板。
定义数据模型
// 用户数据模型
@Data
public class UserModel {
private Integer id;
private String name;
private String email;
}
// 模板数据模型,用于填充 {.xxx} 部分
@Data
public class TemplateModel {
private String reportTime;
private String operator;
private Integer totalUserCount;
}
编写导出服务类
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
public class ExcelExportService {
public void exportWithTemplate(HttpServletResponse response) throws Exception {
// 1. 设置响应头
String fileName = "用户报表_" + System.currentTimeMillis() + ".xlsx";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
// 2. 准备数据
// 2.1 准备模板数据 (用于填充 {.xxx})
TemplateModel templateData = new TemplateModel();
templateData.setReportTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
templateData.setOperator("张三");
templateData.setTotalUserCount(100);
// 2.2 准备列表数据 (用于填充 {{xxx}})
List<UserModel> dataList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
UserModel user = new UserModel();
user.setId(i);
user.setName("用户" + i);
user.setEmail("user" + i + "@example.com");
dataList.add(user);
}
// 3. 获取模板文件流
// 假设模板文件放在 resources/templates 目录下
InputStream templateInputStream = this.getClass().getClassLoader().getResourceAsStream("templates/user_template.xlsx");
// 4. 创建 ExcelWriter
// EasyExcel.write(response.getOutputStream()).withTemplate(templateInputStream).build()
// 这种方式会自动关闭流,推荐使用
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(templateInputStream).build();
// 5. 填充数据
// 5.1 填充模板数据 (填充 {.xxx})
WriteSheet writeSheet = EasyExcel.writerSheet().build();
excelWriter.fill(templateData, writeSheet);
// 5.2 填充列表数据 (填充 {{xxx}})
// 指定从第几行开始填充(根据模板,数据从第7行开始,所以行索引是6)
// EasyExcel 会自动找到 {{}} 标记并循环填充
excelWriter.fill(dataList, writeSheet);
// 6. 关闭流
excelWriter.finish();
}
}
步骤 4:调用导出接口
你可以在一个 Controller 中调用这个服务。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RestController
public class ExportController {
private final ExcelExportService excelExportService;
// 通过构造器注入,推荐方式
public ExportController(ExcelExportService excelExportService) {
this.excelExportService = excelExportService;
}
@GetMapping("/export/users")
public void exportUserExcel(HttpServletResponse response) {
try {
excelExportService.exportWithTemplate(response);
} catch (IOException e) {
// 处理异常,例如记录日志
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
访问 http://localhost:8080/export/users 浏览器就会下载生成的 Excel 文件。
使用注解动态生成模板(无需 .xlsx 文件)
如果你不想维护一个独立的模板文件,也可以使用 EasyExcel 的注解来动态构建“模板”,这种方式更偏向于代码定义样式,但本质上也是先生成一个内存中的模板再填充数据。
步骤 1:定义数据模型并使用注解
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;
@Data行高
@ContentRowHeight(20)行高
@HeadRowHeight(25)
public class UserModelWithAnnotation {
@ExcelProperty("用户ID") // 指定列名
private Integer id;
@ExcelProperty("姓名")
private String name;
@ExcelProperty("邮箱")
private String email;
}
步骤 2:编写导出代码
这种方式不需要模板文件,直接写入数据即可。
import com.alibaba.excel.EasyExcel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@RestController
public class DynamicExportController {
@GetMapping("/export/users/dynamic")
public void exportDynamicUserExcel(HttpServletResponse response) throws IOException {
// 1. 设置响应头
String fileName = "动态用户报表_" + System.currentTimeMillis() + ".xlsx";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
// 2. 准备数据
List<UserModelWithAnnotation> dataList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
UserModelWithAnnotation user = new UserModelWithAnnotation();
user.setId(i);
user.setName("动态用户" + i);
user.setEmail("dynamic" + i + "@example.com");
dataList.add(user);
}
// 3. 直接写入数据
// EasyExcel 会根据 UserWithAnnotationModel 的注解信息自动构建表头和样式
EasyExcel.write(response.getOutputStream(), UserModelWithAnnotation.class)
.sheet("用户列表")
.doWrite(dataList);
}
}
总结与对比
| 特性 | 模板文件导出 | 注解动态导出 |
|---|---|---|
| 适用场景 | 报表格式复杂、样式固定、需要非开发人员维护模板的场景。 | 报表格式相对简单、完全由代码控制、追求开发效率的场景。 |
| 灵活性 | 高,修改模板文件即可改变样式,无需重新编译代码。 | 低,样式修改需要改动代码并重新发布。 |
| 维护成本 | 需要同时维护模板文件和代码。 | 只需维护代码。 |
| 代码复杂度 | 相对复杂,需要处理模板流和数据填充。 | 非常简单,几行代码即可完成。 |
| 样式控制 | 最强,可以在 Excel 中精确控制每个单元格的字体、颜色、边框、合并等。 | 有限,主要通过注解控制,对于复杂样式(如条件格式、复杂合并)支持较弱。 |
最终建议:
- 对于正式的、复杂的业务报表,强烈推荐使用 方案一(模板文件导出),它的灵活性和样式控制能力是方案二无法比拟的。
- 对于简单的数据导出,如导出列表数据,使用 方案二(注解动态导出) 更快捷方便。
希望这个详细的指南能帮助你掌握 Java Excel 模板导出!
