重阳,需求很常见:根据一个已设计好的 Excel 模板(含样式、公式、合并单元格、表头、页眉页脚等),往里面填充动态数据,然后生成新的 Excel 文件。
2026 年主流的三种方案对比(基于当前社区使用情况):
| 方案 | 推荐指数 (2026) | 内存占用 | 模板支持度(样式/合并/公式/图片) | 学习曲线 | 大文件友好度 | Maven 依赖大小 |
|---|---|---|---|---|---|---|
| EasyExcel(阿里) | ★★★★★ | 极低 | 优秀(支持复杂模板填充) | 低 | ★★★★★ | 小 |
| Apache POI | ★★★★☆ | 高 | 最好(几乎全能) | 中 | ★★☆☆☆ | 大 |
| JXLS(基于 POI) | ★★★☆☆ | 中 | 很好(Jxls 模板语法) | 中 | ★★★☆☆ | 中 |
| Fastexcel / Spire.XLS | ★★☆☆☆ | 低/中 | 中等 | 低/高 | ★★★★☆ | 小/商业 |
2026 年最推荐:EasyExcel
理由:内存友好、API 简洁、模板填充功能成熟、社区活跃、Spring Boot 集成极好。
下面直接给你三种主流实现方式的完整代码示例(都基于 Spring Boot 项目场景)。
方案一:EasyExcel 模板填充(最推荐)
Maven 依赖(最新稳定版 3.x 或 4.x 系列,根据 2026 年情况用最新)
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.4</version><!-- 或更高版本 --></dependency>模板准备(excel 模板文件 template.xlsx)
在模板中用{}或自定义占位符,例如:
- 单值:
{date}、{totalAmount} - 列表竖向填充:
{.list}或用{{.开头(EasyExcel 推荐语法)
示例模板内容(A1 放标题,A2 开始表头,B3 开始放{name}、{age}等):
| A | B | C |
|---|---|---|
| 报表标题 | {title} | |
| 姓名 | 年龄 | 得分 |
| {.students} |
代码实现(Controller 或 Service 中)
importcom.alibaba.excel.EasyExcel;importcom.alibaba.excel.write.enums.poi.HorizontalAlignment;importcom.alibaba.excel.write.enums.poi.VerticalAlignment;importcom.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;importjavax.servlet.http.HttpServletResponse;importjava.net.URLEncoder;importjava.nio.charset.StandardCharsets;importjava.util.*;@RestController@RequestMapping("/excel")publicclassExcelController{@GetMapping("/export-template-fill")publicvoidexportWithTemplate(HttpServletResponseresponse)throwsException{// 1. 准备数据(可以从数据库查)Map<String,Object>data=newHashMap<>();data.put("title","2026年班级成绩汇总");data.put("date","2026-02-03");data.put("total",88);List<Map<String,Object>>students=newArrayList<>();students.add(Map.of("name","张三","age",18,"score",95));students.add(Map.of("name","李四","age",19,"score",88));students.add(Map.of("name","王五","age",17,"score",92));data.put("students",students);// 2. 设置响应头(浏览器下载)response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");StringfileName=URLEncoder.encode("成绩报表.xlsx",StandardCharsets.UTF_8.toString()).replaceAll("\\+","%20");response.setHeader("Content-disposition","attachment;filename*=utf-8''"+fileName);// 3. 模板路径(放在 resources/templates/ 下)StringtemplateFileName="templates/成绩模板.xlsx";// 4. 核心:模板填充写出EasyExcel.write(response.getOutputStream()).withTemplate(templateFileName).registerWriteHandler(newLongestMatchColumnWidthStyleStrategy())// 自适应列宽,可选.sheet().doFill(data);// ← 核心方法!传入 map 或对象}}高级用法提示(EasyExcel 模板填充常见技巧):
- 列表横向填充:用
{{.或指定方向 - 多列表:多个
.list占位符 - 复杂对象:
{.user.name}点语法 - 保留样式/公式/合并单元格:EasyExcel 会尽量保留
- 图片填充:支持
FillWrapper+Image对象
方案二:Apache POI 基于模板填充(最灵活,但内存高)
依赖:
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.3.0</version><!-- 或更高 --></dependency>核心代码片段:
importorg.apache.poi.xssf.usermodel.XSSFWorkbook;importjava.io.FileInputStream;importjava.io.OutputStream;// 加载模板try(FileInputStreamfis=newFileInputStream("template.xlsx");XSSFWorkbookworkbook=newXSSFWorkbook(fis)){varsheet=workbook.getSheetAt(0);// 填充单值sheet.getRow(1).getCell(1).setCellValue("2026-02-03 重阳");// 填充列表(从第3行开始)introwIndex=3;for(varstudent:students){varrow=sheet.getRow(rowIndex)!=null?sheet.getRow(rowIndex):sheet.createRow(rowIndex);row.createCell(0).setCellValue(student.getName());row.createCell(1).setCellValue(student.getAge());rowIndex++;}// 输出到 response 或文件workbook.write(response.getOutputStream());}优点:可以精确控制每个单元格的样式、公式、批注、条件格式等。
缺点:代码量大,内存占用高(大文件容易 OOM)。
方案三:快速选择建议
| 场景 | 首选方案 |
|---|---|
| 大数据量(万行+) | EasyExcel |
| 复杂样式/公式/图表保留 | Apache POI |
| 需要极致灵活控制单元格 | Apache POI |
| 快速上手、内存敏感 | EasyExcel |
| 想用模板语法(如 {var}) | EasyExcel / JXLS |
小建议:
- 先准备好模板文件,占位符用
{}或{{}}(EasyExcel 推荐) - 测试时把模板放 resources/templates/ 下,用 classpath 加载
- 生产环境建议把模板上传到文件服务器或放在 jar 外
重阳,你现在是想:
- 用 EasyExcel 做列表多sheet 填充?
- 处理合并单元格 / 图片 / 下拉框?
- 导出后自动调整列宽 / 加水印?
- 还是遇到具体报错想 debug?
告诉我你的具体需求或模板样子,我可以给你更精确的代码~