fputcsv是PHP原生导出CSV唯一推荐函数,自动按RFC 4180转义字段,需配合'wb'模式、UTF-8+BOM、流式写入及header正确设置,避免implode拼接和内存溢出。

用 fputcsv 写入文件最稳妥
PHP 原生导出 CSV,fputcsv 是唯一推荐的函数。它自动处理字段中的逗号、换行、双引号转义,不手动拼接字符串,避免解析错误。
常见错误是用 implode 拼接 CSV 行,比如 implode(',', $row) —— 一旦某字段含逗号或换行(如用户输入的地址、备注),Excel 打开就错列甚至崩溃。
- 必须先用
fopen打开文件句柄,模式选'w'或'wb'(Windows 下推荐'wb'防止换行符异常) - 每行数据传给
fputcsv,它会按 RFC 4180 规则包裹和转义 - 中文需确保输出文件编码为 UTF-8,并在开头写入 BOM(
),否则 Excel 可能乱码
file_put_contents('data.csv', "");
$fp = fopen('data.csv', 'ab');
foreach ($data as $row) {
fputcsv($fp, $row, ',', '"', '\');
}
fclose($fp);
内存大时别用 array_map + implode 拼接
有人想“一行流”生成 CSV 字符串:用 array_map 处理每行再 implode 换行。这看似简洁,但实际会把整个数组加载进内存拼成一个超长字符串,导出 10 万行以上极易 OOM。
fputcsv 是流式写入,一行一写,内存占用恒定;而字符串拼接是累积式,内存随数据量线性增长。
立即学习“PHP免费学习笔记(深入)”;
- 导出超过 5000 行,坚决不用
ob_get_contents或implode构建完整 CSV 字符串 - 如果必须返回字符串(如 API 响应),改用
php://memory临时流,再stream_get_contents获取,比纯字符串拼接安全 -
fputcsv的第 4 个参数(enclosure)默认是",第 5 个(escape_char)默认是,不要轻易改成空字符,否则字段含双引号时会破坏格式
导出到浏览器要设对 header 和输出顺序
直接输出 CSV 给浏览器下载,关键不是内容,而是 HTTP 头和输出时机。header 错或提前 echo 任何字符,就会导致下载失败或文件损坏。
- 必须在
fputcsv开始前调用header,且顺序不能错:Content-Type、Content-Disposition、Cache-Control - 推荐组合:
header('Content-Type: text/csv; charset=utf-8');+header('Content-Disposition: attachment; filename="export.csv"');+header('Cache-Control: no-cache'); - 务必在
header后加ob_end_clean()清掉可能存在的缓冲输出(比如框架日志、warning 输出) - 最后用
fpassthru($fp)输出文件流,而不是readfile,前者更可控,不会因文件权限或路径问题中断
多维数组嵌套或对象数据要先扁平化
fputcsv 只接受一维数组,每个元素是字段值。遇到 ['name' => '张三', 'tags' => ['vip', 'new']] 这类结构,直接传会报 Warning:fputcsv() expects parameter 2 to be array, string given。
不能靠 json_encode 硬塞进去——CSV 不是 JSON 容器,下游系统(尤其是 Excel)根本不会解析字段里的 JSON。
- 明确业务需求:是把 tags 展开成多列(
tags_1,tags_2),还是合并成单字段("vip,new")?前者需提前array_pad对齐列数,后者用implode限长后塞入 - 对象需先
(array)强转,但注意私有属性不可见;更稳的方式是定义toArray()方法控制导出字段 - 时间戳、布尔值、null 要显式转换:
$row['created_at'] = date('Y-m-d H:i:s', $row['created_at']);,否则 CSV 里是数字或空,Excel 无法识别为日期



















