php文件下载失败主因是输出流被干扰或配置不当:需开头加ob_start()、发送头前ob_clean()并禁用错误显示;content-type与content-disposition必须准确前置;大文件应分块读取而非readfile;并严格校验文件路径与权限。

PHP文件下载失败,多数不是代码写错,而是输出流被干扰或配置不当。关键不在“怎么写”,而在“怎么不破坏二进制流”——哪怕只多一个空格、一次意外 echo、一个未关闭的输出缓冲,PDF、ZIP、EXE 等二进制文件就会损坏,浏览器提示“文件已损坏”或根本打不开。
检查并清理输出缓冲
PHP在发送 header 前,任何输出(包括空白符、UTF-8 BOM、warning 信息)都会导致 headers already sent 错误,使 Content-Disposition 失效,文件被当作网页内容直接渲染。
- 脚本开头立即加 ob_start(),统一管理输出
- 正式输出文件前调用 ob_clean() 清空已有缓冲,再 flush() 确保无残留
- 禁用错误报告显示:设置 error_reporting(0) 或 ini_set('display_errors', '0'),避免警告污染流
确保响应头准确且前置
浏览器靠响应头决定是否下载、如何解析。缺一不可,且必须在任何 echo/fread 之前发送。
-
Content-Type 要匹配真实类型:PDF 用
application/pdf,不是application/octet-stream(后者易被拦截或跳过预览) -
Content-Disposition 必须含
attachment,中文名建议用:header("Content-Disposition: attachment; filename*=UTF-8''" . rawurlencode($name)); - Content-Length 必须准确,不能省略——它是前端进度计算和断点续传的基础
改用流式读取,别碰 readfile
readfile 会把整个文件加载进内存,超 100MB 就容易超限或超时,尤其在共享主机或低配环境。它不支持进度控制,也不容错。
- 用 fopen('rb') 打开文件,配合 fread($fp, 8192) 分块读取(8KB 是较稳妥值)
- 每次读完立刻 echo + ob_flush() + flush(),强制推送,防止缓冲堆积
- 循环中加入 if (connection_aborted()) break; 检测用户中断,及时释放资源
验证文件路径与权限是否干净
看似下载逻辑没问题,实则卡在第一步:文件根本没读到。
- 用 file_exists() 和 is_readable() 双重校验,不要只靠 basename 拼接路径
- 禁止用户传入含
../的参数,做基础过滤:$safe_name = basename($_GET['f']); - 检查 Web 服务器(如 Nginx/Apache)是否对目标目录有读取权限,特别是 SELinux 或 Docker 容器内路径映射问题
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











