Workerman协程中大文件上传需流式分块处理:禁用$_FILES解析,用php://memory配合磁盘转存,检查磁盘空间;通过upload_id追踪状态并超时熔断;推荐WebSocket分片上传,每帧确认并加LOCK_EX追加写入。

Workerman协程中直接处理大文件上传容易触发内存溢出、协程栈耗尽或事件循环阻塞,导致进程崩溃或连接中断。
改用流式分块接收,禁用一次性读取
第一步:在 Worker 启动前,设置 $_SERVER['REQUEST_METHOD'] = 'POST' 并禁用 PHP 默认的 $_FILES 全局变量解析——因为 Workerman 不走 PHP-FPM,$_FILES 为空且强行调用会引发 Notice 或干扰协程调度。
第二步:监听 onMessage 事件时,对 $connection->getRemoteIp() 做连接标识,配合 ConnectionInterface::onClose 清理未完成上传的临时缓冲区。
第三步:收到数据后不拼接完整 body,而是用 fopen('php://memory', 'rw+') 创建内存流,每次 fwrite($stream, $data) 写入,达到阈值(如 8MB)立即 fflush($stream) 并转存到磁盘临时文件,同时重置流指针位置。
这一步不可跳过:【必须在写入磁盘前检查磁盘剩余空间,否则 fwrite 成功但 sync 失败时协程会卡死】
启用上传状态追踪与超时熔断
方法一:为每个上传连接分配唯一 upload_id = md5($ip . time() . rand(1000,9999)),将当前已收字节数、开始时间、临时路径存入 $_SESSION(需用 Redis 驱动的 Session)或本地 tmp/ 下带时间戳的 JSON 文件。
方法二:在 onMessage 中每收到一次数据就更新该 upload_id 对应的状态,并用 time() - $start_time > 300 判断是否超时;超时则自动关闭连接并删除临时文件。
注意:不要用 sleep() 或 usleep() 做等待,协程里 sleep 会阻塞整个进程。
切换到 WebSocket 协议分片上传
WebSocket 天然支持二进制帧和自定义分片逻辑,比 HTTP POST 更可控。
客户端按 512KB 分片发送,每帧携带 {seq: 1, total: 24, md5: 'xxx', data: Uint8Array};服务端用 $connection->send(json_encode(['status'=>'ok','seq'=>1])) 实时确认,丢失帧可重传。
服务端收到完整分片后,用 file_put_contents($tmp_file, $data, FILE_APPEND | LOCK_EX) 追加写入,避免多协程并发写冲突。
【追加写必须加 LOCK_EX,否则多个分片同时写会导致文件内容错乱】

















