workerman需流式解析multipart/form-data边界并分片处理,因$_files不生效、php上传配置无效且php://input不支持分块;安全接收分片须提取boundary、逐行扫描定位字段、精确截取payload写入临时文件,并用file_id/chunk_index实现幂等校验与断点续传。

Workerman 本身不解析 multipart/form-data,大文件上传必须手动处理原始请求体——直接走流式读取 + 分片校验,否则必爆内存或丢数据。
为什么不能用 $_FILES 或框架自动解析?
Workerman 是纯异步 TCP/HTTP 服务器,不依赖 PHP-FPM,$_FILES 在这里根本不会被填充。所有上传数据都裸露在 $request->rawBody() 或 $connection->recv() 中,靠自己拆解边界(boundary)。一旦用错方式(比如等完整 body 收完再处理),50MB 文件就可能让 Worker 进程卡死或 OOM。
- PHP 的
upload_max_filesize和post_max_size对 Workerman 无效——这些是 PHP-FPM 的配置,Workerman 完全绕过 - 试图用
file_get_contents('php://input')会失败:它只对一次性的短请求有效,且不支持分块传输(chunked encoding) - 真实场景中,前端往往用
fetch+FormData发送分片,但 Workerman 收到的是“一整段带 boundary 的二进制流”,不是结构化数组
怎么安全接收一个分片?关键三步
分片上传不是“接收文件”,而是“按协议提取一段二进制 payload”。核心在于识别 boundary、定位字段、截取 blob —— 全程流式,不落地、不缓存全文本。
- 从
$request->header('content-type')提取boundary=...,构造正则或字节扫描器 - 用
fgets()或stream_get_line()逐行读 body 流,跳过 header 行,直到遇到--{boundary}和Content-Disposition: form-data; name="chunk"; filename="..." - 真正有效的分片数据从下一个
\r\n\r\n开始,到下一个--{boundary}前结束;用fread($stream, $length)精确截取,避免读多或读少 - 建议把分片写入临时文件(如
/tmp/upload_{file_id}_{index}.part),而不是存在内存里——Worker 进程重启后还能续传
onMessage 里直接处理分片?小心并发冲突
Workerman 的 onMessage 是单连接单线程回调,但多个客户端同时上传时,不同连接的分片可能并发写同一个文件名(比如都叫 video.mp4),导致覆盖或损坏。
- 必须用唯一标识区分上传会话:
file_id(前端传的全局 hash)、chunk_index(分片序号)、total_chunks(总片数)缺一不可 - 不要用
file_put_contents(..., FILE_APPEND)拼接分片——它不是原子操作,高并发下会乱序;应独立保存每个.part,合并阶段再按序 cat - 合并前务必校验每个分片的
md5(前端计算并随分片上传),否则网络丢包或中间代理篡改会导致静默损坏 - 合并完成后,用
rename()替换目标文件,避免“半成品”被其他进程读到
断点续传怎么判断“这个分片我收过了”?
不是查数据库,而是查文件系统——简单、快、不依赖外部服务。
- 约定存储路径为
/upload/{file_id}/chunks/{index}.part,收到分片前先file_exists()检查 - 如果已存在,跳过写入,直接返回
{"code":200,"msg":"already received"}(前端据此跳过该分片) - 注意权限:Workerman 进程用户必须对
/upload目录有读写权,且禁用open_basedir限制(否则file_exists失败) - 别用 MySQL 记录分片状态——IO 成瓶颈,且单点故障会让整个上传链路瘫痪
真正的难点不在“怎么收”,而在“怎么确认没丢、没重、没乱”。分片哈希、文件系统级幂等、流式边界识别——这三环漏一环,上传就不可靠。很多人卡在 boundary 解析失败却去调 PHP 配置,方向错了。










