Workerman处理大文件上传不能依赖$_FILES和php://input,必须通过$request->rawBody()或$connection->recv()获取原始二进制流,手动流式解析multipart边界、逐行定位payload、精确截取写入,并用file_id/chunk_index隔离并发、Redis幂等校验、cat命令合并防内存溢出。
Workerman 处理大文件上传不能依赖
,必须手动解析原始请求体;流式传输的核心是不把整段数据读进内存,而是边收边截、边写边校验——否则 50MB 文件就可能让 Worker 进程卡死或 OOM。
为什么
和
都失效?
Workerman 是常驻进程的异步服务器,不走 PHP-FPM 生命周期,所以
永远为空;
在 Workerman 下不可用,它只对 CGI/FPM 环境有效,且不支持分块传输(chunked encoding)。
所有上传数据都裸露在:
(HTTP 协议栈下)
或
(TCP 自定义协议下)
你拿到的是一整段带
的二进制流,不是结构化数组。
怎么安全提取 multipart 分片?三步流式定位
关键不是“解析表单”,而是“按 HTTP 协议逐行扫描边界,精确截取 payload”。不能等 body 收全再处理,必须流式推进。
Workerman
最新主版本,继续强化高性能异步网络能力,适合 WebSocket、HTTP 服务、长连接、IM、IoT 等场景。
本次版本主要围绕稳定性、协议兼容以及连接处理能力优化,依旧保持超轻量和纯 PHP 特性。
适用于 PHP 8.x 环境,可配合 webman、GatewayWorker 等生态使用。
下载
从
header 提取
,构造匹配字符串(如
)
用
或
逐行读原始 body 流,跳过 header 行,直到遇到
和
真正有效的文件数据从下一个
开始,到下一个
前结束;用
精确读取,长度需根据前后 boundary 位置动态计算
示例片段(非完整代码,仅示意逻辑):
并发写入和断点续传怎么防冲突?
多个客户端同时上传同名文件,或者同一文件分片重试,极易覆盖或乱序。必须靠元数据隔离 + 幂等控制。
前端必须传三个关键字段:
(全局唯一,如文件 MD5)、
(从 0 开始)、
每个分片独立写入临时路径:
,不要用
拼接——它不是原子操作
合并前检查所有
是否齐全,并逐个校验
(前端随分片提交,服务端流式重算比对)
Redis 中存
标记已接收,重复请求直接返回 200
合并命令建议用
,而不是 PHP 的
全量加载。
最容易被忽略的是临时文件生命周期管理:Workerman 不会自动清理
,你得自己加定时任务或 onWorkerStop 回调删过期
;还有中文文件名必须从
转回 UTF-8,否则
类逻辑会失败。
$_FILES$_FILESphp://input$_FILESphp://input$request->rawBody()$connection->recv()boundaryContent-Typeboundary=...--1234567890fgets()stream_get_line()--{boundary}Content-Disposition: form-data; name="file"; filename="xxx"\r\n\r\n--{boundary}fread($stream, $length)while (!feof($body_stream)) {
$line = fgets($body_stream);
if (preg_match('/^--' . preg_quote($boundary) . '--$/', $line)) break;
if (strpos($line, 'filename="') !== false) {
// 找到字段头,下一行必为 \r\n\r\n,之后就是 payload
fgets($body_stream); // 跳过空行
$chunk = fread($body_stream, $expected_size);
file_put_contents($tmp_path, $chunk, FILE_APPEND | LOCK_EX);
}
}file_idchunk_indextotal_chunks/tmp/upload_{file_id}_{chunk_index}.partFILE_APPEND.partmd5{file_id}:{chunk_index}cat /tmp/upload_abc_0.part /tmp/upload_abc_1.part > /final/file.zipfile_get_contentsupload_tmp_dir.partISO-8859-1move_uploaded_file