正确写法是先设置Content-Type、Content-Disposition(filename用rawurlencode+UTF-8''前缀)、Content-Length头,再ob_end_clean()关闭缓冲,最后readfile()输出;否则中文名乱码、截断或无法下载。

readfile 下载文件的正确写法
直接用 readfile 输出文件内容是常见做法,但不加头信息会导致浏览器当成页面内容渲染,而不是触发下载。必须手动设置响应头,且顺序和内容不能错。
- 先调用
header('Content-Type: application/octet-stream')(或更精确的 MIME 类型,如application/pdf) - 必须设置
Content-Disposition,推荐用attachment; filename="xxx",注意文件名需用rawurlencode处理中文(否则乱码或截断) - 加上
Content-Length(用filesize()获取)能提升大文件体验,避免浏览器卡在“正在等待”状态 - 务必在
readfile()前关闭输出缓冲(ob_end_clean()),否则可能混入空格或警告导致 header 失效
中文文件名下载失败的典型原因
浏览器对 Content-Disposition 中的中文支持不一,PHP 默认不转义,直接拼接会导致 Chrome 下文件名为空、Safari 下乱码、Firefox 下被截断。不能只靠 mb_convert_encoding 或简单 urlencode。
- 推荐方案:用
rawurlencode($filename)+UTF-8''编码前缀,例如:header('Content-Disposition: attachment; filename="UTF-8\'\'' . rawurlencode($filename) . '"'); - 如果服务端 PHP 版本 filename*= 和
filename=双写(但多数现代项目已无需) - 避免从用户输入直接取文件名,必须校验路径(如用
basename()防止目录穿越)
readfile 和 file_get_contents 的性能差异
二者都能输出文件,但机制不同:readfile 是边读边输出,内存占用恒定;file_get_contents 会把整个文件读进内存再 echo,大文件容易 OOM。
- 10MB 以上文件必须用
readfile,不要图省事换函数 -
readfile返回的是字节数,可用于日志记录或异常判断(返回false表示读取失败) - 如果文件在远程(如
https://开头),readfile不支持,得换curl或流式处理,这不是它设计场景
常见 Header 错误导致下载失败
哪怕逻辑都对,一个错位的 header 就会让下载变成白屏或报错。最容易忽略的是已有输出(空格、BOM、warning)让 header 发送失败。
立即学习“PHP免费学习笔记(深入)”;
- 检查 PHP 文件是否以 UTF-8 无 BOM 格式保存(尤其 Windows 编辑器常带 BOM)
- 确保没有
echo、print、var_dump在 header 之前执行 - 开启
display_errors = Off(线上环境必须关),否则 PHP 警告会提前输出,破坏 header - 用
headers_sent($file, $line)快速定位哪行代码导致 header 失败



















