PHP实现动态文件下载时速度大幅变慢的问题求助
问题原因排查及解决方案
核心原因(按概率从高到低排序)
- PHP输出缓冲未正确关闭
默认PHP的输出缓冲(output_buffering)是开启状态,调用readfile时文件内容会先写入PHP缓冲区,填满后才会推送至Web服务器再返回给客户端,小包频繁发送直接拉低传输效率。 - zlib输出压缩被意外开启
如果php.ini中zlib.output_compression = On,PHP会对所有输出内容自动做压缩处理,你传输的zip本身已经是高压缩率文件,重复压缩只会无谓消耗CPU,拉长传输时间。 - readfile读取块大小默认值过小
未自定义读取块大小时,PHP每次读取的文件片段很小,磁盘IO次数过多也会拖慢速度。 - (次要影响)PHP-FPM进程重载异常
你提供的日志中多次出现Another FPM instance seems to already listen on /tmp/php-cgi-73.sock错误,说明重载配置时旧进程未正常退出,残留进程占用socket资源,高并发时会导致请求排队,也可能间接影响下载速度。
对应解决方法
1. 调整下载脚本,关闭缓冲并优化输出
修改你的下载脚本如下:
<?php $file = './French-Stories-Martine.zip'; if (headers_sent()) { echo 'HTTP header already sent'; } else { if (!is_file($file)) { header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found'); echo 'File not found'; } else if (!is_readable($file)) { header($_SERVER['SERVER_PROTOCOL'].' 403 Forbidden'); echo 'File not readable'; } else { // 关闭所有层级的输出缓冲 while (ob_get_level()) ob_end_clean(); // 临时禁用输出压缩 if (ini_get('zlib.output_compression')) ini_set('zlib.output_compression', 'Off'); header($_SERVER['SERVER_PROTOCOL'].' 200 OK'); header("Content-Type: application/zip"); header("Content-Transfer-Encoding: Binary"); header("Content-Length: ".filesize($file)); header("Content-Disposition: attachment; filename=\"".basename($file)."\""); readfile($file); exit; } } ?>
2. 调整PHP配置优化传输
编辑对应版本的php.ini文件:
- 若全局业务没有依赖输出缓冲的场景,可直接将
output_buffering设置为Off,否则仅在下载类脚本中临时关闭即可 - 确认
zlib.output_compression默认设置为Off,如果全局需要开启,必须在下载类脚本中临时关闭 - 保持
output_handler为空,避免额外的输出处理逻辑拖慢速度
3. 修复PHP-FPM重载异常问题
执行以下操作清理残留进程:
- 停止PHP-FPM服务:
systemctl stop php-fpm73(根据你实际的服务名调整) - 手动删除残留的sock文件:
rm -f /tmp/php-cgi-73.sock - 检查是否有残留PHP-FPM进程:
ps aux | grep php-fpm,如有残留进程直接kill即可 - 重新启动PHP-FPM:
systemctl start php-fpm73
之后再重载配置就不会出现socket占用的错误。
4. 进阶优化方案(大文件下载场景)
如果下载的文件普遍超过100M,可以使用X-Sendfile功能(Apache、OpenLiteSpeed均原生支持),PHP只做权限校验,文件传输完全交给Web服务器处理,性能和静态下载完全一致:
// 权限校验通过后添加以下头即可,不需要手动调用readfile输出 header("X-Sendfile: " . realpath($file)); header("Content-Type: application/zip"); header("Content-Disposition: attachment; filename=\"".basename($file)."\""); exit;
注意需要提前在Web服务器配置中开启X-Sendfile支持。
内容的提问来源于stack exchange,提问作者Patira
相关产品推荐
相关产品推荐

