Workerman怎么上传大文件_分片接收与流式处理技巧【技巧】

2次阅读

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

Workerman怎么上传大文件_分片接收与流式处理技巧【技巧】

Workerman 本身不解析 multipart/form-data,大文件上传必须手动处理原始请求体——直接走流式读取 + 分片校验,否则必爆内存或丢数据。

为什么不能用 $_FILES 或框架自动解析?

Workerman 是纯异步 TCP/http 服务器,不依赖 PHP-FPM,$_FILES 在这里根本不会被填充。所有上传数据都裸露在 $request->rawBody()$connection->recv() 中,靠自己拆解边界(boundary)。一旦用错方式(比如等完整 body 收完再处理),50MB 文件就可能让 Worker 进程卡死或 OOM。

  • PHP 的 upload_max_filesizepost_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="..."
  • 真正有效的分片数据从下一个 rnrn 开始,到下一个 --{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 配置,方向错了。

text=ZqhQzanResources