您的位置:首页 >Java+EasyExcel实现单个接口导出多个Excel的示例详解
发布于2026-05-03 阅读(0)
扫一扫,手机访问
在日常开发中,导出Excel是家常便饭,通常一个接口对应一个文件。但偶尔会遇到一些特殊场景——用户希望一次点击,就能同时拿到“用户列表”和“订单列表”这两份独立的数据报表。这该怎么实现呢?
这里有一个关键的技术前提需要明确:HTTP协议规定,单次响应只能返回一个字节流。这意味着,一个接口无法直接返回两个独立的文件。那么,如何绕过这个限制,满足用户“一次下载,多份文件”的需求呢?
答案其实很经典:打包。将多个Excel文件打包成一个ZIP压缩包,接口返回这个ZIP流。用户下载后解压,就能得到所有独立的文件。这个方案既完全遵守HTTP规范,又完美解决了业务需求,是业界通用且稳妥的做法。
工欲善其事,必先利其器。实现这个功能,我们需要三个核心依赖:处理Excel读写的EasyExcel、负责文件打包的Apache Commons Compress,以及构建Web接口的Spring Boot Web Starter。以下是Ma ven配置,使用Gradle的朋友可以自行转换。版本号建议与示例保持一致,以避免潜在的兼容性问题。
com.alibaba easyexcel 4.0.3 org.apache.commons commons-compress 1.27.1 org.springframework.boot spring-boot-starter-web
数据是Excel的灵魂,而实体类就是数据的蓝图。假设我们要导出用户和订单两份数据,就需要为它们分别创建实体类。通过EasyExcel的@ExcelProperty注解,可以轻松定义最终在Excel表头中显示的名称。这里使用Lombok的@Data注解来简化代码,省去手写getter/setter的麻烦。
import 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;
}
import 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;
}
为了避免在每一个导出接口里重复编写打包和设置响应头的代码,封装一个通用的工具类是明智之举。这个ExcelZipExportUtil工具类将核心逻辑收拢,后续无论导出多少个Excel文件,都可以直接调用,代码复用率极高。
import com.alibaba.excel.EasyExcel;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import ja vax.servlet.ServletOutputStream;
import ja vax.servlet.http.HttpServletResponse;
import ja va.io.ByteArrayOutputStream;
import ja va.net.URLEncoder;
import ja va.util.List;
/**
* EasyExcel 多文件导出 ZIP 工具类(通用可复用)
*/
public class ExcelZipExportUtil {
/**
* 将单个 Excel 文件写入 ZIP 输出流
* @param zipOut ZIP 输出流
* @param excelFileName 单个 Excel 的文件名(如:用户列表.xlsx)
* @param data Excel 中的数据列表
* @param clazz Excel 对应的实体类(用于解析表头)
*/
public static void writeExcelToZip(ZipArchiveOutputStream zipOut, String excelFileName,
List data, Class 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");
}
}
工具类准备好后,接口层的实现就变得非常清晰。在Controller中,我们模拟构造数据(实际项目替换为数据库查询),然后调用工具类完成打包和响应。整个过程一气呵成,用户通过一次请求就能下载到包含多个Excel的ZIP包。
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 ja vax.servlet.http.HttpServletResponse;
import ja va.util.ArrayList;
import ja va.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 的数据(用户列表,实际项目中替换为数据库查询)
List userList = new ArrayList<>();
// 3.1 完善数据逻辑省略
// 4. 构造第二个 Excel 的数据(订单列表,实际项目中替换为数据库查询)
List 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() -> zipOut.finish() -> 关闭其他流的顺序操作。顺序一旦颠倒,生成的ZIP包很可能无法正常解压。URLEncoder.encode(fileName, "UTF-8")对文件名进行编码,这是确保在Chrome、Firefox、Edge等所有主流浏览器中都能正确显示中文名的标准做法。ByteArrayOutputStream在内存中生成Excel内容,避免了不必要的磁盘I/O操作,性能更优。不推荐先写入临时磁盘文件再打包的方式。printStackTrace()仅用于演示。在生产环境中,务必使用全局异常处理器或更优雅的方式,向前端返回友好的错误信息,例如“导出失败,请稍后重试”,以提升用户体验。这个方案的扩展性极佳。如果需要导出三个、四个甚至更多Excel文件,完全无需修改工具类,只需在接口中继续调用ExcelZipExportUtil.writeExcelToZip()方法即可。
例如,新增一个“商品列表”的导出:
// 新增商品列表数据(假设已有 GoodsData 实体类) ListgoodsList = new ArrayList<>(); // ... 构造商品数据 // 新增一个 Excel 写入 ZIP 流 ExcelZipExportUtil.writeExcelToZip(zipOut, "商品列表.xlsx", goodsList, GoodsData.class);
代码完成后,可以通过以下几个步骤验证功能是否正常:
http://localhost:8080/export/twoExcel(端口号请根据实际配置调整);回顾整个方案,其实可以清晰地归纳为三个步骤:
该方案设计通用,可直接复用于任何Spring Boot项目,能够支持任意数量的Excel文件导出。同时,它已经规避了流关闭顺序、中文乱码等常见“坑点”。如果你在项目中遇到类似的多文件导出需求,不妨直接采用这套经过验证的代码方案。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
9