跳转到主内容
极星编程网:以代码为星,赴技术山海!

Workerman怎么处理大文件上传?流式传输怎么实现?

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

相关文章