杰瑞科技汇

Java Excel导出模板,如何动态填充数据?

  1. 保持样式一致:预先设计好标题、字体、颜色、边框、Logo、合并单元格等复杂样式。
  2. 简化代码:无需在 Java 代码中硬编码单元格样式、行高、列宽等,代码更简洁。
  3. 提高灵活性:非开发人员(如产品经理、运营)可以轻松修改模板,而无需改动代码。

下面我将为你提供一个非常详细和实用的 Java Excel 导出模板方案,以目前最流行的 EasyExcel 为例,并包含完整的代码示例。

Java Excel导出模板,如何动态填充数据?-图1
(图片来源网络,侵删)

为什么选择 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 的特定规则来标记数据填充的位置。

  1. 创建一个 Excel 文件,user_template.xlsx
  2. 在需要填充数据的地方,使用 语法来定义占位符。
    • 填充集合数据,会自动循环创建行。
    • 填充单个对象数据。
    • {.属性名}:填充对象的具体属性值。

示例模板 user_template.xlsx

Java Excel导出模板,如何动态填充数据?-图2
(图片来源网络,侵删)

你可以用 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} 表示填充一个 MapPOJOreportTime 属性的值。
  • 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 模板导出!

分享:
扫描分享到社交APP
上一篇
下一篇