🚀 Excel 导出与导入如何封装为工具类 —— 一名 Java 老程序员的实战分享

51 阅读3分钟

🚀 Excel 导出与导入如何封装为工具类 —— 一名 Java 老程序员的实战分享

作者:一个在业务代码和工具类之间反复横跳的 8 年 Java 工程师


🧠 写在前面

如果你和我一样,在 Java 开发中频繁遇到“导入导出 Excel”的需求,那么你一定也写过这样的代码:

  • controller 里 copy paste 一段导出逻辑;
  • service 层里嵌套 try-catch 操作 stream;
  • 每次导入导出都得重复造轮子;
  • 改个模板字段,改得心态爆炸 💥。

So,我决定把这些“业务无关但高频使用”的逻辑封装成一个通用的 Excel 工具类,只做一件事:让导入导出像呼吸一样简单自然。


🧰 技术选型

选择比努力更重要。

我们采用的是当前社区广泛使用的 EasyExcel(阿里开源):

  • 🚀 轻量,性能优;
  • 🧩 支持注解配置,代码可读性强;
  • 🤝 社区活跃,文档完善。

Maven 引入方式如下:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
</dependency>

📦 工具类封装目标

我们希望的效果是这样的:

  • 导出 Excel:
ExcelUtil.export(response, list, "用户信息", UserExportVO.class);
  • 导入 Excel:
List<UserImportVO> list = ExcelUtil.importExcel(file, UserImportVO.class);

是不是很丝滑?


🧱 工具类设计思路

我们将工具类分为两块:

  1. 导出工具类(Export)
  2. 导入工具类(Import)

同时,支持以下特性:

  • 支持设置 Sheet 名、文件名;
  • 自动处理响应 Header;
  • 支持异常捕捉;
  • 支持多 Sheet 导出(可扩展);
  • 支持模版填充(进阶)。

📤 导出工具类核心代码

public class ExcelUtil {

    /**
     * 导出 Excel 文件
     *
     * @param response  HttpServletResponse
     * @param list      数据列表
     * @param fileName  文件名
     * @param clazz     数据类型
     * @param <T>       泛型
     */
    public static <T> void export(HttpServletResponse response, List<T> list, String fileName, Class<T> clazz) {
        try {
            // 设置响应头
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            String encodeFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename=" + encodeFileName + ".xlsx");

            // 写入数据
            EasyExcel.write(response.getOutputStream(), clazz)
                    .autoCloseStream(true)
                    .sheet("Sheet1")
                    .doWrite(list);

        } catch (IOException e) {
            throw new RuntimeException("Excel 导出失败", e);
        }
    }
}

📥 导入工具类核心代码

public class ExcelUtil {

    /**
     * 从 Excel 文件中读取数据
     *
     * @param file  上传的文件
     * @param clazz 数据类型
     * @param <T>   泛型
     * @return 数据列表
     */
    public static <T> List<T> importExcel(MultipartFile file, Class<T> clazz) {
        try {
            ExcelListener<T> listener = new ExcelListener<>();
            EasyExcel.read(file.getInputStream(), clazz, listener).sheet().doRead();
            return listener.getData();
        } catch (IOException e) {
            throw new RuntimeException("Excel 导入失败", e);
        }
    }

    // 内部监听器类
    private static class ExcelListener<T> extends AnalysisEventListener<T> {
        private final List<T> data = new ArrayList<>();

        @Override
        public void invoke(T t, AnalysisContext context) {
            data.add(t);
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            // no-op
        }

        public List<T> getData() {
            return data;
        }
    }
}

✅ 使用示例

导出 Controller 示例

@GetMapping("/export")
public void exportUser(HttpServletResponse response) {
    List<UserExportVO> list = userService.getAllUsers();
    ExcelUtil.export(response, list, "用户列表", UserExportVO.class);
}

导入 Controller 示例

@PostMapping("/import")
public String importUser(@RequestParam MultipartFile file) {
    List<UserImportVO> list = ExcelUtil.importExcel(file, UserImportVO.class);
    userService.saveBatch(list);
    return "导入成功,共 " + list.size() + " 条数据";
}

🔍 实体类注解说明

EasyExcel 使用注解来标注 Excel 字段:

@Data
public class UserExportVO {

    @ExcelProperty("用户ID")
    private Long id;

    @ExcelProperty("用户名")
    private String name;

    @ExcelProperty("邮箱")
    private String email;
}

🧩 拓展建议

  • ✅ 支持多 Sheet 导出;
  • ✅ 支持模板导出(模板 + 数据填充);
  • ✅ 支持导入校验(如手机号格式、必填字段);
  • ✅ 支持异常数据行记录;
  • ✅ 封装为 Spring Boot Starter,提升复用度。

🧘 总结

写工具类的终极目标,是让业务代码更 专注业务

封装一个高复用的 Excel 工具类,不但提升了开发效率,还减少了重复劳动。作为一个有 8 年经验的 Java 程序员,我越来越意识到:

优雅,往往来自于对细节的极致追求。