一个优雅、通用、零侵入的 CSV 导出工具类(Java 实战)

在业务系统中,CSV 导出几乎是一个绕不开的需求。

看起来很简单,但真正做过的人一定踩过这些坑:

  • Excel 打开 CSV,中文乱码
  • 字段里有逗号、引号,整行错位
  • 每个导出都写一套 DTO,代码重复又丑
  • 工具类和业务对象强绑定,复用性极差

最近在项目中,我整理了一个通用 CSV 导出工具类,使用下来非常顺手,索性分享出来,或许也能帮到你。


一、设计目标

在写这个工具类之前,我给自己定了几个目标:

  1. 不依赖任何具体业务对象
  2. 支持任意 DTO / VO / 聚合对象
  3. Excel 直接打开,不出现中文乱码
  4. CSV 格式严格、安全,不怕特殊字符
  5. 使用方式要足够简单

最终的实现,也基本满足了这些要求。


二、核心设计思路

1️⃣ 使用泛型 + Function 映射行数据

核心方法签名如下:

java 复制代码
public static <T> String exportCsv(
        List<String> headers,
        List<T> dataList,
        Function<T, List<Object>> rowMapper)

这里的关键点是 Function<T, List<Object>>

  • 工具类 完全不知道 T 是什么
  • 每一行如何映射,由调用方决定
  • 不侵入 DTO,也不需要额外注解

这种设计方式在公共工具类中非常友好。


2️⃣ 主动写入 UTF-8 BOM,解决 Excel 中文乱码

java 复制代码
sb.append("\uFEFF");

这是一个非常容易被忽略、但极其重要的细节。

在 Windows 环境下,Excel 对 UTF-8 的识别并不稳定,不写 BOM,中文大概率乱码

写入 BOM 几乎没有成本,却能解决 90% 的问题,非常值得。


3️⃣ CSV 字段统一加双引号,彻底避免格式问题

java 复制代码
return "\"" + val.replace("\"", "\"\"") + "\"";

CSV 的坑主要来自:

  • 字段中包含逗号 ,
  • 包含双引号 "
  • 包含换行符

这里采用的是 RFC 4180 标准做法

  • 所有字段统一加双引号
  • 字段内的 " 转义为 ""

简单、粗暴、但极其稳定。


三、完整工具类代码

java 复制代码
package com.fang.industry.service.common.utils;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * CSV工具类
 */
public class CsvUtils {

    /**
     * 通用CSV导出方法
     *
     * @param headers   表头列表(中文)
     * @param dataList  数据源列表
     * @param rowMapper 行数据映射函数
     */
    public static <T> String exportCsv(
            List<String> headers,
            List<T> dataList,
            Function<T, List<Object>> rowMapper) {

        StringBuilder sb = new StringBuilder();

        // 1. 写入 BOM,防止 Excel 打开中文乱码
        sb.append("\uFEFF");

        // 2. 写入表头
        sb.append(String.join(",", headers)).append("\n");

        // 3. 写入数据行
        if (dataList != null) {
            for (T data : dataList) {
                List<Object> fields = rowMapper.apply(data);
                String rowStr = fields.stream()
                        .map(CsvUtils::formatCsvField)
                        .collect(Collectors.joining(","));
                sb.append(rowStr).append("\n");
            }
        }
        return sb.toString();
    }

    /**
     * 格式化 CSV 字段
     */
    private static String formatCsvField(Object field) {
        if (field == null) {
            return "";
        }
        String val = field.toString();
        return "\"" + val.replace("\"", "\"\"") + "\"";
    }
}

四、使用示例

示例 1:导出普通 DTO 列表

数据对象
java 复制代码
@Data
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String remark;
}

导出 CSV
java 复制代码
List<String> headers = List.of("用户ID", "姓名", "年龄", "备注");

List<User> users = List.of(
        new User(1L, "张三", 18, "正常用户"),
        new User(2L, "李四", 25, "包含,逗号"),
        new User(3L, "王五", 30, "包含\"引号\"")
);

String csv = CsvUtils.exportCsv(
        headers,
        users,
        user -> List.of(
                user.getId(),
                user.getName(),
                user.getAge(),
                user.getRemark()
        )
);

生成的 CSV 用 Excel 打开完全正常。


示例 2:Spring Boot 接口直接下载 CSV

java 复制代码
@GetMapping("/export")
public void exportCsv(HttpServletResponse response) throws IOException {

    List<String> headers = List.of("ID", "姓名", "年龄");
    List<User> users = userService.list();

    String csv = CsvUtils.exportCsv(
            headers,
            users,
            u -> List.of(u.getId(), u.getName(), u.getAge())
    );

    response.setContentType("text/csv;charset=UTF-8");
    response.setHeader("Content-Disposition",
            "attachment; filename=\"users.csv\"");

    response.getWriter().write(csv);
}

五、适用场景 & 扩展思考

这个工具类特别适合:

  • 后台管理系统 CSV 导出
  • 临时分析数据导出
  • 中小数据量(非百万级)场景

如果数据量特别大,可以进一步优化为:

  • 使用 Writer 流式写出
  • 或直接对接文件下载流

六、总结

这个 CSV 工具类的核心特点是:

  • 通用:不绑定任何业务对象
  • 安全:彻底规避 CSV 格式坑
  • 实用:Excel 打开即用,不乱码

如果你也在项目中经常写 CSV 导出,不妨试试这种写法,或许能让你的工具类库更干净一些。


如果你觉得有用,欢迎点赞、收藏。

后面如果有机会,也会继续分享一些真正来自业务一线的实战代码

相关推荐
宠..8 小时前
写一个感染型病毒
开发语言·安全·安全性测试
cike_y8 小时前
JavaWeb-Request应用与Cookie&[特殊字符]️Session
java·开发语言·安全·java安全
hashiqimiya9 小时前
两个步骤,打包war,tomcat使用war包
java·服务器·前端
大筒木老辈子9 小时前
C++笔记---并发支持库(atomic)
java·c++·笔记
Cricyta Sevina9 小时前
Java Collection 集合进阶知识笔记
java·笔记·python·collection集合
胡萝卜3.09 小时前
深入C++可调用对象:从function包装到bind参数适配的技术实现
开发语言·c++·人工智能·机器学习·bind·function·包装器
小a杰.9 小时前
Flutter 设计系统构建指南
开发语言·javascript·ecmascript
BD_Marathon9 小时前
【JavaWeb】Servlet_url-pattern的一些特殊写法问题
java·开发语言·servlet