找回密码
 立即注册
首页 业界区 业界 老码农教你:Solon + EasyExcel 导出工具

老码农教你:Solon + EasyExcel 导出工具

染悄 2025-9-28 18:28:56
关于 "Excel 导出" ——POI API 是比较复杂的,CellStyle 能把人调得眼冒金星,大数据量导出时内存飙到 90% 的恐惧至今难忘。直到发现了 Alibaba 的 EasyExcel,从此打开新世界的大门。今天就把这套 "导出救命锦囊" 分享给大家,顺便穿插点踩坑经验。
一、先整合项目环境

1. 引入依赖

首先在 pom.xml 里加依赖,这里得注意版本兼容性。加完后记得刷新 Maven。
  1. <dependency>
  2.     <groupId>org.noear</groupId>
  3.     solon-web</artifactId>
  4.     <version>3.5.0</version>
  5. </dependency>
  6. <dependency>
  7.     <groupId>com.alibaba</groupId>
  8.     easyexcel</artifactId>
  9.     <version>4.0.3</version>
  10. </dependency>
复制代码
2. 创建实体类

定义 Excel 里每一列的数据结构,就像给每个字段安排 "座位"。比如导出用户信息:
  1. import com.alibaba.excel.annotation.ExcelProperty;
  2. import com.alibaba.excel.annotation.format.DateTimeFormat;
  3. import lombok.Data;
  4. import java.util.Date;
  5. @Data
  6. public class UserExcelVO {
  7.     // 这里是表头名称,宽度设置成20
  8.     @ExcelProperty(value = "用户ID", index = 0)
  9.     @ColumnWidth(20)
  10.     private Long userId;
  11.     // 设定日期为"yyyy-MM-dd"格式
  12.     @ExcelProperty(value = "注册时间", index = 1)
  13.     @DateTimeFormat("yyyy-MM-dd")
  14.     private Date registerTime;
  15.     // 性别要转换为友好描述
  16.     @ExcelProperty(value = "性别", index = 2, converter = SexConverter.class)
  17.     private Integer sex;
  18. }
复制代码
这里的 @ExcelProperty 就像给数据贴标签,index是列顺序。别标错号,不然数据错位时会怀疑人生 —— 笔者曾把金额和年龄的位置搞反,会被财务小姐姐骂得狗血淋头。
3. 编写导出工具类:避免重复工作

把通用导出逻辑封装起来,以后每次导出就简单了。创建EasyExcelUtils:
  1. import com.alibaba.excel.EasyExcel;
  2. import org.noear.solon.core.handle.Context;
  3. import java.io.IOException;
  4. import java.net.URLEncoder;
  5. import java.util.List;
  6. public class EasyExcelUtils {
  7.     public static <T> void exportExcel(Context ctx,
  8.                                        List<T> dataList,
  9.                                        Class<T> clazz,
  10.                                        String fileName) throws IOException {
  11.         ctx.contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  12.         ctx.charset("utf-8");
  13.         // 文件名得处理中文,不然下载下来是乱码
  14.         fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
  15.         ctx.headerSet("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
  16.         // 这里用EasyExcel.write()就像启动一个Excel生成器
  17.         EasyExcel.write(ctx.outputStream(), clazz)
  18.                 .sheet("数据报表")
  19.                 .doWrite(dataList);
  20.     }
  21. }
复制代码
二、实战演练:从 "基础导出" 到 "高深玩法"

1. 基础导出
  1. import org.noear.solon.annotation.*;
  2. import org.noear.solon.core.handle.Context;
  3. import java.io.IOException;
  4. import java.util.List;
  5. @Controller
  6. public class DemoController {
  7.     @Inject
  8.     UserService userService;
  9.     @Get
  10.     @Mapping("/exportUser")
  11.     public void exportUser(Context ctx) throws IOException {
  12.         List<UserExcelVO> dataList = userService.getUserListForExport(); // 假设这是从数据库查的数据
  13.         EasyExcelUtils.exportExcel(ctx, dataList, UserExcelVO.class, "用户信息表");
  14.     }
  15. }
复制代码
2. 复杂表头,加一层分类

有时候表头需要多级结构,比如 "用户信息" 下分 "基本信息"" 联系方式 "。这时候需要用@ExcelProperty的数组形式:
  1. import com.alibaba.excel.annotation.ExcelProperty;
  2. import lombok.Data;
  3. @Data
  4. public class ComplexHeaderVO {
  5.     @ExcelProperty({"用户信息", "用户ID"})
  6.     private Long userId;
  7.     @ExcelProperty({"用户信息", "姓名"})
  8.     private String userName;
  9.     @ExcelProperty({"联系方式", "手机号"})
  10.     private String phone;
  11.     @ExcelProperty({"联系方式", "邮箱"})
  12.     private String email;
  13. }
复制代码
3. 合并单元格

比如导出报表时需要合并相同内容的单元格,这时候得自定义CellWriteHandler。举个例子,合并连续相同的部门名称:
  1. import com.alibaba.excel.write.handler.CellWriteHandler;
  2. import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
  3. public class MergeCellHandler implements CellWriteHandler {
  4.     @Override
  5.     public void afterCellDispose(CellWriteHandlerContext context) {
  6.         // 这里省略具体实现,核心是通过行号和列号判断是否合并
  7.         // 就像拼拼图,找到相同的部分粘在一起
  8.     }
  9. }
复制代码
在导出时注册这个处理器:
  1. EasyExcel.write(...)
  2.     .registerWriteHandler(new MergeCellHandler())
  3.     .doWrite(...);
复制代码
4. 自定义格式:美化效果

比如金额需要显示成 "¥1,000.00",日期要显示成 "2025 年 5 月 29 日"。除了前面提到的 @DateTimeFormat,数值格式可以用 @NumberFormat:
  1. @ExcelProperty("金额")
  2. @NumberFormat("#,##0.00")
  3. private Double amount;
复制代码
5. 大数据量导出,要避免 OOM

当数据量超过 10 万条时,直接导出会OOM的,这时候要用流式处理。EasyExcel 贴心地支持分页导出,分批次写入:
  1. EasyExcel.write(ctx.outputStream(), UserExcelVO.class)
  2.     .sheet("大数据报表")
  3.     .registerWriteHandler(...) // 可选的样式处理器
  4.     .doWrite(new AnalysisContext() -> {
  5.         // 这里每次调用获取一页数据,直到没有数据为止
  6.         List<UserExcelVO> pageData = userService.getPageData(analysisContext.readRowHolder().getRowIndex());
  7.         return pageData;
  8.     });
复制代码
三、避坑指南

1. 依赖冲突:当 Maven 开始 "闹别扭"

如果有引入旧版 POI 依赖,可能会和 EasyExcel 的 POI 版本冲突。这时候可用 mvn dependency:tree 命令排查,然后在pom.xml里用排除冲突项。
2. 注解优先级:别让 "标签" 打架

@ExcelProperty 可以写在字段上或方法上,建议统一写在字段上,不然容易混乱。
3. 样式设置:别把 Excel 变成 "花脸猫"

虽然 EasyExcel 支持自定义样式,但别过度使用,比如给每个单元格设置不同颜色,导出的 Excel 可能打不开。样式设置要适度,就像化妆,自然美就好。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册