您现在的位置是:首页 > 文章详情

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

日期:2025-08-26点击:59

关于 "Excel 导出" ——POI API 是比较复杂的,CellStyle 能把人调得眼冒金星,大数据量导出时内存飙到 90% 的恐惧至今难忘。直到发现了 FastExcel(前身是 Alibaba 的 EasyExcel),从此打开新世界的大门。今天就把这套 "导出大法" 分享给大家,顺便穿插点踩坑经验。

一、先整合项目环境

1. 引入依赖

首先在 pom.xml 里加依赖,这里得注意版本兼容性。fastexcel 最新版本移除了 poi 的依赖,大好特好!

<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-web</artifactId>
    <version>3.5.0</version>
</dependency>

<dependency>
    <groupId>cn.idev.excel</groupId>
    <artifactId>fastexcel</artifactId>
    <version>1.3.0</version>
</dependency>

2. 创建实体类

定义 Excel 里每一列的数据结构,就像给每个字段安排 "座位"。比如导出用户信息:

import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.format.DateTimeFormat;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import lombok.Data;

import java.util.Date;

@Data
public class UserExcelVO {
    // 这里是表头名称,宽度设置成20
    @ExcelProperty(value = "用户ID", index = 0)
    @ColumnWidth(20)
    private Long userId;

    // 设定日期为"yyyy-MM-dd"格式
    @ExcelProperty(value = "注册时间", index = 1)
    @DateTimeFormat("yyyy-MM-dd")
    private Date registerTime;

    // 性别要转换为友好描述
    @ExcelProperty(value = "性别", index = 2, converter = SexConverter.class)
    private Integer sex;
}

这里的 @ExcelProperty 就像给数据贴标签,index是列顺序。别标错号,不然数据错位时会怀疑人生 —— 笔者曾把金额和年龄的位置搞反,会被财务小姐姐骂得狗血淋头。

3. 编写导出工具类:避免重复工作

把通用导出逻辑封装起来,以后每次导出就简单了。创建EasyExcelUtils:

import cn.idev.excel.EasyExcel;
import org.noear.solon.core.handle.Context;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;

public class EasyExcelUtils {
    public static <T> void exportExcel(Context ctx,
                                       List<T> dataList,
                                       Class<T> clazz,
                                       String fileName) throws IOException {
        ctx.contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        ctx.charset("utf-8");
        // 文件名得处理中文,不然下载下来是乱码
        fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        ctx.headerSet("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        // 这里用EasyExcel.write()就像启动一个Excel生成器
        EasyExcel.write(ctx.outputStream(), clazz)
                .sheet("数据报表")
                .doWrite(dataList);
    }
}

二、实战演练:从 "基础导出" 到 "高深玩法"

1. 基础导出

import org.noear.solon.annotation.*;
import org.noear.solon.core.handle.Context;

import java.io.IOException;
import java.util.List;

@Controller
public class DemoController {
    @Inject
    UserService userService;

    @Get
    @Mapping("/exportUser")
    public void exportUser(Context ctx) throws IOException {
        List<UserExcelVO> dataList = userService.getUserListForExport(); // 假设这是从数据库查的数据
        EasyExcelUtils.exportExcel(ctx, dataList, UserExcelVO.class, "用户信息表");
    }
}

2. 复杂表头,加一层分类

有时候表头需要多级结构,比如 "用户信息" 下分 "基本信息"" 联系方式 "。这时候需要用@ExcelProperty的数组形式:

import cn.idev.excel.annotation.ExcelProperty;
import lombok.Data;

@Data
public class ComplexHeaderVO {
    @ExcelProperty({"用户信息", "用户ID"})
    private Long userId;

    @ExcelProperty({"用户信息", "姓名"})
    private String userName;

    @ExcelProperty({"联系方式", "手机号"})
    private String phone;

    @ExcelProperty({"联系方式", "邮箱"})
    private String email;
}

3. 合并单元格

比如导出报表时需要合并相同内容的单元格,这时候得自定义CellWriteHandler。举个例子,合并连续相同的部门名称:

import cn.idev.excel.write.handler.CellWriteHandler;
import cn.idev.excel.write.handler.context.CellWriteHandlerContext;

public class MergeCellHandler implements CellWriteHandler {
    @Override
    public void afterCellDispose(CellWriteHandlerContext context) {
        // 这里省略具体实现,核心是通过行号和列号判断是否合并
        // 就像拼拼图,找到相同的部分粘在一起
    }
}

在导出时注册这个处理器:

EasyExcel.write(...)
    .registerWriteHandler(new MergeCellHandler())
    .doWrite(...);

4. 自定义格式:美化效果

比如金额需要显示成 "¥1,000.00",日期要显示成 "2025 年 5 月 29 日"。除了前面提到的 @DateTimeFormat,数值格式可以用 @NumberFormat:

@ExcelProperty("金额")
@NumberFormat("#,##0.00")
private Double amount;

5. 大数据量导出,要避免 OOM

当数据量超过 10 万条时,直接导出会OOM的,这时候要用流式处理。FastExcel 贴心地支持分页导出,分批次写入:

EasyExcel.write(ctx.outputStream(), UserExcelVO.class)
    .sheet("大数据报表")
    .registerWriteHandler(...) // 可选的样式处理器
    .doWrite(new AnalysisContext() -> {
        // 这里每次调用获取一页数据,直到没有数据为止
        List<UserExcelVO> pageData = userService.getPageData(analysisContext.readRowHolder().getRowIndex());
        return pageData;
    });

三、避坑指南

1. 注解优先级:别让 "标签" 打架

@ExcelProperty 可以写在字段上或方法上,建议统一写在字段上,不然容易混乱。

2. 样式设置:别把 Excel 变成 "花脸猫"

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

原文链接:https://www.oschina.net/news/368571
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章