react/stream 仅提供流接口抽象,不内置文件分块、背压控制或自动流式读写能力,需手动组合 fopen/fread 与事件循环实现可控处理。

react/stream 本身不提供“自动流式读写超大文件”的能力,它只是 ReactPHP 的底层流抽象层。真正要处理超大文件,你得自己组合 fs(PHP 原生)或 React\Stream\ReadableResourceStream/WritableResourceStream,并配合事件循环。直接装 react/stream 后调用 file_get_contents() 或 fread($fp, 1024*1024),照样内存爆炸。
为什么不能只装 react/stream 就能流式处理大文件?
react/stream 定义的是 ReadableStreamInterface 和 WritableStreamInterface 接口,以及基础的资源包装器(比如包装 fopen() 返回的 resource)。但它不封装文件 IO 逻辑,也不帮你分块、不处理 EOF、不管理缓冲区大小——这些都得你手写。
常见误解是:“装了 stream 就有流式文件 API”,其实它连 createReadStream 这种函数都没有。Node.js 那套开箱即用的 fs.createReadStream 在 PHP + ReactPHP 里不存在。
- 它不替代
fopen()/fread(),只是把它们“事件化”包装一层 - 没内置背压控制:你
write()太快而下游没消费,会直接丢数据或触发 warning - 错误处理靠手动监听
error事件,不像原生fread()那样返回false可立即判断
怎样用 react/stream 包装 fopen 实现可控流式读取?
核心是:用 fopen('large.bin', 'rb') 打开,再用 React\Stream\ReadableResourceStream 包一层,然后在 data 事件里做分块处理。但注意——你仍要自己控制每次 fread() 的 size,否则默认可能一次读几 MB。
示例关键片段:
$fp = fopen('huge-file.dat', 'rb');
if (!$fp) {
throw new RuntimeException('Cannot open file');
}
$loop = React\EventLoop\Factory::create();
$stream = new React\Stream\ReadableResourceStream($fp, $loop);
$stream->on('data', function ($chunk) {
// $chunk 是 fread() 返回的一块二进制数据,大小由底层 resource 决定
// 但你无法直接控制这块多大!所以更稳妥的做法是:
// → 改用自定义循环 + fread($fp, 8192),再手动 emit('data', $buf)
});
$stream->on('end', function () {
fclose($fp);
});
$loop->run();
- 别依赖
ReadableResourceStream的默认行为来“自动分块”——它只是把stream_select()封装了,对普通文件句柄,data事件可能一触发就吐出整个 GB 文件(取决于系统缓冲) - 真要可控,得自己写循环:
fread($fp, 8192)+$stream->emit('data', [$buf]) - 必须显式
fclose(),ReadableResourceStream不自动关文件
实际项目中更推荐的替代路径
如果你目标只是“安全读写超大文件”,不强求异步事件驱动,那 react/stream 是过度设计。PHP 原生流已足够:
- 用
fopen()+fread()+while循环,块大小设为8192或65536,内存占用稳定在 KB 级 - 需要异步 HTTP 上传/下载?优先考虑
react/http+React\Stream\ThroughStream做管道转换,而非硬套文件流 - 真要 ReactPHP 全栈异步文件处理?搭配
react/filesystem(非官方,需第三方)或自己封装stat()+ 分块readfile()+yield协程
顺带提醒:composer require react/stream 后,记得同时装 react/event-loop,否则 Factory::create() 会报错——react/stream 不自动拉取事件循环。
容易被忽略的关键点
所有基于 react/stream 的文件操作,本质还是在 stream_select() 上跑,而它对普通磁盘文件句柄的支持很弱:Linux 下会退化成轮询,Windows 下甚至不可用。这意味着——你以为的“异步读文件”,其实大部分时间是同步阻塞的,只是被包了一层事件壳。
真正需要高并发文件 IO 的场景(如同时处理数百个大日志),应该用进程池(parallel 扩展)或移交到 Go/Python 子进程,而不是在 PHP 里死磕 react/stream。


















