CodeIgniter 导出CSV为空是因header被提前输出干扰;需清空输出缓冲、用fopen('php://output')流式输出、加UTF-8 BOM、禁用profiler及自动输出类。

CodeIgniter 导出 CSV 文件为空,基本就是 header() 被提前输出干扰了——不是逻辑错,是输出缓冲没管住。
为什么导出的 CSV 文件是空的
CodeIgniter 默认开启输出缓存,控制器里任何 echo、var_dump()、模板中多余的空格、甚至模型里多打的一个换行,都会在 header() 之前把响应体“冲出去”。一旦 HTTP 头部被发送,再调用 header('Content-Disposition: attachment...') 就完全失效,浏览器收到的是一个没头、没内容的响应,自然下出来是空文件。
- 常见错误现象:
Warning: Cannot modify header information - headers already sent by... - 容易被忽略的位置:视图文件开头的 BOM、
config/autoload.php末尾多了一个空行、模型方法里写了print_r($data)调试但忘了删 - 验证方式:在导出方法最开头加
if (headers_sent($file, $line)) { die("headers sent at {$file}:{$line}"); }
用 fopen('php://output', 'w') 直接流式输出
绕过文件写入+读取两步,直接把 CSV 写进响应流,更干净、更可控,也避免权限/路径问题。
- 不要先写到磁盘再
readfile():既慢又可能因路径或权限失败 - 必须在
header()之后立刻fopen('php://output', 'w'),且不能有任何输出穿插其间 -
fputcsv()会自动处理字段中的逗号、换行、双引号,比手动拼接安全得多 - 记得加 UTF-8 BOM(
\xEF\xBB\xBF):否则 Windows Excel 打开中文会乱码
示例片段:
使用 qbo-mileage CLI 及用户凭证,从 Airtable、Outlook 或 Google Calendar 记录生成 QuickBooks Online 里程 CSV 文件。
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="export_' . date('Ymd_His') . '.csv"');
header('Pragma: public');
header('Expires: 0');
<p>$output = fopen('php://output', 'w');
fputcsv($output, ['姓名', '邮箱', '注册时间']); // 表头</p><p>foreach ($data as $row) {
fputcsv($output, $row);
}
fclose($output);
exit;导出前务必关闭 CodeIgniter 的输出缓存
CI 的 $this->output->enable_profiler(FALSE) 或全局禁用 profiler 只是表象;真正要掐断的是框架级输出缓冲链。
- 在导出方法开头立即执行:
ob_end_clean();(清空并关闭当前输出缓冲) - 如果用了 CI 的
output类,加一句:$this->output->set_output('');防止它后续追加内容 - 确保没有加载任何会自动输出的库(比如某些日志类、调试工具类)
- 开发时可临时在
index.php顶部加ob_start();,但生产环境不推荐——应从源头清理输出
中文字段乱码和 Excel 打不开的兼容处理
不是所有“UTF-8”都一样。Excel(尤其旧版)只认 utf-8-sig(即带 BOM 的 UTF-8),且对 Content-Type 中的 charset 参数敏感。
- 必须输出 BOM:
echo "\xEF\xBB\xBF";放在fopen('php://output', 'w')之前 -
Content-Type必须写全:text/csv; charset=utf-8,不能只写text/csv - 避免用
iconv('utf-8', 'gbk', ...):GBK 是区域编码,跨平台不可靠;BOM + UTF-8 是通用解 - 字段含特殊字符(如换行符)时,
fputcsv()自动加双引号,不用自己str_replace()
最关键的不是“怎么写”,而是“怎么不让别的东西抢在你前面输出”。导出功能越简单,越要盯紧输出缓冲——它不报错,只默默让你下出一个 0 字节的文件。

















