Java + EasyExcel 实现单个接口导出多个Excel

📅 2026/6/24 4:11:57
Java + EasyExcel 实现单个接口导出多个Excel
一、核心问题与解决方案首先要明确一个关键前提HTTP 协议单次响应只能返回一个字节流无法直接返回两个独立的 Excel 文件相当于一次请求只能下载一个文件。那怎么实现“一个接口导出多个 Excel”答案很简单——将多个 Excel 文件打包成 ZIP 压缩包接口返回 ZIP 流用户下载后解压就能得到多个 Excel 文件。这是最通用、最合规的解决方案既不违背 HTTP 协议也能满足用户一次性获取多份文件的需求。二、前置准备引入依赖首先在项目中引入 EasyExcel 核心依赖、ZIP 压缩依赖用于打包多文件以及 Spring Web 依赖接口开发必备。以下是 Maven 配置Gradle 可对应转换版本可根据自己项目需求微调建议保持和示例一致避免兼容问题。!-- EasyExcel 核心依赖 -- dependency groupIdcom.alibaba/groupId artifactIdeasyexcel/artifactId version4.0.3/version /dependency !-- ZIP 压缩依赖处理多文件打包 -- dependency groupIdorg.apache.commons/groupId artifactIdcommons-compress/artifactId version1.27.1/version /dependency !-- Spring Web 依赖已有则忽略接口开发必备 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency三、步骤1定义Excel对应的实体类假设我们需要导出两个 Excel 文件用户列表和订单列表分别创建对应的实体类通过 EasyExcel 的 ExcelProperty 注解指定 Excel 表头名称注解参数就是最终 Excel 中显示的表头。实体类用 Lombok 的 Data 注解简化 getter/setter 代码无需手动编写节省开发时间。3.1 用户实体类UserData.javaimport com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; /** * 用户列表 Excel 对应的实体类 */ Data public class UserData { // Excel 表头用户ID ExcelProperty(用户ID) private Long userId; // Excel 表头用户名称 ExcelProperty(用户名称) private String userName; // Excel 表头手机号 ExcelProperty(手机号) private String phone; }3.2 订单实体类OrderData.javaimport com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; /** * 订单列表 Excel 对应的实体类 */ Data public class OrderData { // Excel 表头订单ID ExcelProperty(订单ID) private String orderId; // Excel 表头用户ID关联用户表 ExcelProperty(用户ID) private Long userId; // Excel 表头订单金额 ExcelProperty(订单金额) private Double amount; // Excel 表头创建时间 ExcelProperty(创建时间) private String createTime; }四、步骤2封装通用工具类核心为了避免接口层代码冗余我们封装一个工具类 ExcelZipExportUtil专门处理“将多个 Excel 写入 ZIP 流”和“初始化 HTTP 响应头”的逻辑。这个工具类是通用的后续不管导出多少个 Excel都能直接复用无需重复编码。import com.alibaba.excel.EasyExcel; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.net.URLEncoder; import java.util.List; /** * EasyExcel 多文件导出 ZIP 工具类通用可复用 */ public class ExcelZipExportUtil { /** * 将单个 Excel 文件写入 ZIP 输出流 * param zipOut ZIP 输出流 * param excelFileName 单个 Excel 的文件名如用户列表.xlsx * param data Excel 中的数据列表 * param clazz Excel 对应的实体类用于解析表头 */ public static T void writeExcelToZip(ZipArchiveOutputStream zipOut, String excelFileName, ListT data, ClassT clazz) throws Exception { // 1. 临时存储 Excel 内容内存级不写入磁盘性能更高 ByteArrayOutputStream bos new ByteArrayOutputStream(); // 2. 使用 EasyExcel 写入数据sheet1 是工作表名称可自定义 EasyExcel.write(bos, clazz) // 不要自动关闭交给 Servlet 自己处理 .autoCloseStream(false) // 基于 column 长度自动适配。最大 255 宽度 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 避免 Long 类型丢失精度 .registerConverter(new LongStringConverter()) // 工作表名称 .sheet(sheet1) .doWrite(data); // 3. 将 Excel 作为 ZIP 的一个条目写入 zipOut.putArchiveEntry(new ZipArchiveEntry(excelFileName)); zipOut.write(bos.toByteArray()); zipOut.closeArchiveEntry(); // 关闭当前 ZIP 条目必须否则后续条目无法写入 // 4. 关闭临时流 bos.close(); } /** * 初始化 HTTP 响应头设置 ZIP 下载、解决中文文件名乱码 * param response 响应对象 * param zipFileName 最终下载的 ZIP 压缩包名称如用户订单数据.zip */ public static void initZipResponse(HttpServletResponse response, String zipFileName) throws Exception { // 设置响应类型为 ZIP response.setContentType(application/zip); // 设置下载头URLEncoder.encode 解决中文文件名乱码兼容所有浏览器 response.setHeader(Content-Disposition, attachment;filename URLEncoder.encode(zipFileName, UTF-8)); // 禁止缓存避免浏览器缓存旧文件 response.setHeader(Pragma, no-cache); response.setHeader(Cache-Control, no-cache); } }五、步骤3接口层实现最终落地创建 Controller 接口模拟构造两个 Excel 的测试数据实际项目中这里可以替换成从数据库查询数据然后调用上面封装的工具类将两个 Excel 打包成 ZIP 流通过 HttpServletResponse 返回实现“一次请求下载两个 Excel”。import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; /** * Excel 导出接口控制器 */ RestController RequestMapping(/export) public class ExcelExportController { /** * 单个接口导出两个 Excel 文件打包成 ZIP 下载 * 访问地址http://localhost:8080/export/twoExcel */ GetMapping(/twoExcel) public void exportTwoExcel(HttpServletResponse response) { try { // 1. 初始化响应头设置 ZIP 压缩包名称用户下载时显示的文件名 ExcelZipExportUtil.initZipResponse(response, 用户订单数据.zip); // 2. 获取 HTTP 响应输出流关联 ZIP 输出流 ServletOutputStream servletOut response.getOutputStream(); ZipArchiveOutputStream zipOut new ZipArchiveOutputStream(servletOut); // 3. 构造第一个 Excel 的数据用户列表实际项目中替换为数据库查询 ListUserData userList new ArrayList(); // 3.1 完善数据逻辑省略 // 4. 构造第二个 Excel 的数据订单列表实际项目中替换为数据库查询 ListOrderData orderList new ArrayList(); // 4.1 完善数据逻辑省略 // 5. 关键操作将两个 Excel 分别写入 ZIP 流 ExcelZipExportUtil.writeExcelToZip(zipOut, 用户列表.xlsx, userList, UserData.class); ExcelZipExportUtil.writeExcelToZip(zipOut, 订单列表.xlsx, orderList, OrderData.class); // 6. 关闭流顺序不能错否则 ZIP 包会损坏无法解压 zipOut.finish(); // 完成 ZIP 写入 zipOut.close(); servletOut.flush(); servletOut.close(); } catch (Exception e) { e.printStackTrace(); // 实际项目中建议自定义异常处理给前端返回明确的错误提示 response.setStatus(500); } } }六、关键注意事项避坑重点这部分一定要仔细看很多新手实现后出现 ZIP 包损坏、中文乱码、数据缺失等问题大多是因为忽略了这些细节。流的关闭顺序必须先执行 zipOut.closeArchiveEntry()关闭当前 ZIP 条目再执行 zipOut.finish()最后关闭 ZIP 流和响应流。顺序颠倒会导致 ZIP 包损坏无法解压。中文文件名乱码通过 URLEncoder.encode(zipFileName, UTF-8) 对 ZIP 文件名和 Excel 文件名编码兼容 Chrome、Firefox、Edge 等所有主流浏览器避免中文乱码。Excel 写入方式示例中使用 ByteArrayOutputStream 临时存储 Excel 内容属于内存级写入不占用磁盘空间性能更高。不建议直接写入磁盘文件再打包会增加磁盘 IO 开销。异常处理实际项目中建议替换 try-catch 中的 printStackTrace()使用全局异常处理器给前端返回明确的错误信息如“导出失败请重试”提升用户体验。七、扩展导出更多Excel文件如果需要导出 3 个、4 个甚至更多 Excel 文件无需修改工具类只需在接口中继续调用 ExcelZipExportUtil.writeExcelToZip() 方法即可。示例新增“商品列表”Excel// 新增商品列表数据假设已有 GoodsData 实体类 ListGoodsDatagt; goodsList new ArrayList(); // ... 构造商品数据 // 新增一个 Excel 写入 ZIP 流 ExcelZipExportUtil.writeExcelToZip(zipOut, 商品列表.xlsx, goodsList, GoodsData.class);八、测试效果验证代码写完后启动 Spring Boot 项目通过以下步骤测试确保导出正常访问接口地址http://localhost:8080/export/twoExcel端口号根据自己项目配置调整浏览器会自动弹出下载提示下载的文件名为“用户订单数据.zip”解压 ZIP 压缩包会得到两个 Excel 文件用户列表.xlsx 和 订单列表.xlsx打开 Excel 文件检查表头和数据是否正常和接口中构造的测试数据一致。九、总结代码通用可复用总结下来就是 3 步引入 EasyExcel 和 ZIP 依赖做好前置准备定义 Excel 对应的实体类封装通用工具类处理 ZIP 打包和响应头在接口中构造数据调用工具类将多个 Excel 写入 ZIP 流返回给前端。