php处理大文件必须用流式方式,因file_get_contents()会全量加载致内存溢出,而fopen()+fread()按需读取、及时释放,内存可控;fgets逐行读需注意换行符、空行判断及显式fclose防泄漏;生成器yield可将内存压至单行级别;http下载须控制输出缓冲并正确flush;断点续传需自行实现容错机制。

PHP处理大文件,不能靠file_get_contents()或file()——内存立刻爆掉,进程常被系统kill。必须用流式(stream)方式,核心是保持句柄打开、按需拉取、及时释放。
为什么fopen + fread比file_get_contents安全
因为fopen()只分配文件指针和少量缓冲区,fread()每次最多读指定字节数(比如8192),不预加载全量内容;而file_get_contents()会把整个文件塞进内存字符串,GB级文件直接触发Allowed memory size exhausted错误。
- Windows下必须用
'rb'模式,否则\r\n会被自动转成\n,二进制文件(如ZIP、PDF)会损坏 -
fread($fp, 8192)返回值可能是false(读取失败)、''(EOF)、或任意长度的字符串(哪怕只有1字节),不能假设一定返回8192 - 循环中必须显式检查
if ($buf === false) break;和if ($buf === '') break;,否则可能死循环或漏数据
fgets逐行读大文件时容易踩的坑
适合日志、CSV、JSON Lines等文本格式,但默认行为很“脆”:
-
fgets()在Windows下用'r'即可,别用'rb',否则可能跳过换行符导致读错行 - 返回
false表示出错或EOF,不是空行;空行返回的是"\n"或"\r\n",要用trim()判断是否真为空 - 每行末尾自带换行符,后续
json_decode()前得先trim(),否则解析失败 - 没调
fclose($fp)会导致文件句柄泄漏,Linux默认上限1024个,批量任务几轮就崩
生成器yield让内存占用降到单行级别
当你要对每行做独立处理(比如解析JSON、写入数据库、过滤字段),用生成器比手动fgets循环更干净,且天然防内存堆积:
- 函数内用
fopen()打开,while (!feof($fp))中yield trim(fgets($fp)),fclose()放在finally或函数末尾 - 调用方用
foreach (readLines($path) as $line),每次迭代只有一行在内存里 - 注意:
yield函数返回的是Generator对象,不能直接json_encode(),也不能传给count() - 如果某行超长(比如10MB的JSON),
fgets()可能失败,此时应切回fread()分块+手动行切分
HTTP下载大文件必须控制输出缓冲
浏览器下载卡住、连接中断、响应超时,往往不是PHP脚本慢,而是输出层堵住了:
- 必须提前发
Content-Length头(filesize($path)),否则Nginx/Apache可能启用chunked编码,客户端无法显示进度 -
echo fread($fp, 8192); ob_flush(); flush();三者缺一不可:echo写入PHP输出缓冲,ob_flush()清用户缓冲,flush()推给Web服务器 - CLI模式下
flush()无效,Web环境还要确认output_buffering = Off或设为0,否则flush()不起作用 - 超大文件建议加
set_time_limit(0),并用ignore_user_abort(true)防用户中途关页面中断传输
真正难的不是“怎么读”,而是“读到一半出错了怎么办”——比如网络中断、磁盘满、权限突变。流式操作没有原子性,必须自己设计断点续传逻辑或校验机制,这部分没法靠函数自动兜底。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











