Yii2中sendFile不触发下载因未设置Content-Disposition响应头,需手动添加;中文文件名须rawurlencode编码;小文件用sendFile,大文件需Nginx配合streamFile;推荐先生成临时Excel再sendFile并立即unlink。

Yii2 中用 sendFile 输出 Excel 文件为什么浏览器不下载?
因为没设对响应头,sendFile 本身只负责读文件+发二进制流,不自动加 Content-Disposition。浏览器看到 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 这类 MIME 类型,可能直接渲染(尤其 Chrome 对 .xlsx 尝试预览),而不是弹保存框。
- 必须手动调用
Response::setHeader('Content-Disposition', 'attachment; filename="xxx.xlsx"') -
sendFile的第三个参数是配置数组,其中'mimeType'只影响Content-Type,不碰Content-Disposition - 如果文件路径含中文,
filename值要用rawurlencode()编码,否则 Safari 和部分旧版 IE 会乱码或拒收
用 sendFile 还是 streamFile?
sendFile 适合小到中等 Excel(比如 ≤5MB),它把整个文件读进内存再输出;streamFile 是边读边发,内存占用低,但要求 Web 服务器(如 Nginx)配合启用 X-Sendfile 或 X-Accel-Redirect,否则退化成普通读取——而 Yii 默认不启用这些,实际效果和 sendFile 一样。
- 开发环境(Apache + PHP CLI/CGI):直接用
sendFile更稳,不用额外配服务器 - 生产环境且文件常超 10MB:确认 Nginx 已配
location ~ ^/files/ { internal; }并在代码里用streamFile+'xSendFile' => true - 别在
sendFile后写exit或die—— Yii 的sendFile内部已调exit,重复调会导致空白响应
生成 Excel 的时机:控制器里直接写还是先存临时文件?
推荐先写临时文件再 sendFile。虽然有人用 php://output 流式生成,但在 Yii 控制器里容易被框架的输出缓冲、布局模板、中间件干扰,尤其开启 Gzip 或调试工具时,Excel 二进制会被损坏。
- 用
PhpOffice\PhpSpreadsheet\Writer\Xlsx写入sys_get_temp_dir() . '/report_' . uniqid() . '.xlsx' - 生成完立刻
sendFile,然后unlink()—— 不要等到 afterAction,万一用户关浏览器,文件就残留了 - 别把 Excel 直接 echo 到响应体:Yii 的
Response对象默认启用输出缓冲,且可能注入 HTML 模板头尾,导致 Excel 打不开
常见错误现象和对应检查点
导出后 Excel 打开提示“文件格式错误”或“内容与扩展名不匹配”,90% 是响应头或内容污染问题。
- 报错
The file is damaged and cannot be opened:检查有没有意外输出,比如控制器 action 顶部有空格、var_dump、未捕获的 Notice - 下载的文件名变成
index.php:忘了设Content-Disposition头,或设成了inline - 文件大小比预期小很多(如总是 2KB):临时文件路径写错,
sendFile找不到文件,返回了 404 页面的 HTML 内容 - PHP 报
Cannot modify header information:说明之前已有输出,检查是否有echo、print、未关闭的 PHP 标签末尾换行
临时文件路径、响应头顺序、错误抑制(@unlink)这些细节,线上跑得越久越容易漏掉。别信“本地能跑就行”。










