PHP处理大文件必须用流式方式,即fopen()配合fread()/fwrite()控制缓冲区大小;需指定二进制模式、确保目录存在、校验读写返回值,并注意ftell/fseek限制及超时检测。

PHP 处理大文件不能用 file_get_contents() 或 file_put_contents() —— 内存会爆,进程可能被 kill。必须用流式(stream)方式逐块读写,核心是 fopen() 配合 fread() / fwrite() 控制缓冲区大小。
用 fopen() 打开大文件时必须指定正确的模式和上下文
二进制模式('rb' / 'wb')是底线,尤其在 Windows 上避免换行符自动转换导致内容错乱;如果要追加写入且文件可能不存在,别直接用 'ab'——它不创建父目录,得提前 mkdir();需要超时控制或 HTTP 流时,必须传 $context 参数,比如设置 timeout 防止卡死:
-
fopen('huge.log', 'rb')安全读取,不触发自动编码转换 -
fopen('/var/log/app/data.bin', 'wb')覆盖写入前确保目录存在 - HTTP 场景下用
stream_context_create(['http' => ['timeout' => 30]])配合fopen()
fread() 的长度参数不是“尽量读”,而是“最多读这么多”
很多人以为 fread($fp, 8192) 会等满 8192 字节才返回,其实只要底层有数据、或遇到 EOF/网络断连,它就立刻返回已读字节(可能远少于 8192)。所以必须检查返回值是否为 false(错误)或 ''(EOF),不能只靠循环次数判断:
while (($buf = fread($fp, 4096)) !== false) {
if ($buf === '') break; // 显式检测 EOF
fwrite($out, $buf);
}
另外,4096–8192 是较稳妥的块大小:太小(如 1)性能差;太大(如 1MB)可能触发 PHP 内存限制或 OOM。
立即学习“PHP免费学习笔记(深入)”;
写入大文件时,fwrite() 返回值比成功与否更重要
fwrite() 成功返回**实际写入字节数**,不是布尔值。网络文件系统(NFS)、满盘、权限不足时,它可能只写入部分数据就返回(比如请求写 4096,只写 2048),但不报错。必须校验返回值是否等于期望长度:
- 写入失败或部分写入时,
fwrite()返回false或小于预期的整数 - 建议封装成带重试的写函数,例如对临时文件写入后
fflush()+fsync()确保落盘 - 不要依赖
fclose()报错——它只反映句柄关闭状态,不保证之前所有fwrite()都真正刷到磁盘
流式处理中容易被忽略的三个底层细节
一是 ftell() 在某些流(如 php://input、压缩流)上不可用或返回 -1;二是 fseek() 对只读 HTTP 流无效,尝试会静默失败;三是 stream_get_meta_data($fp)['timed_out'] 比检查 fread() 返回值更能准确定位超时问题。这些细节不出现在文档首页,但线上出问题时往往卡在这里。



















