workerman可通过自定义tcp/websocket协议实现跨平台im文件异步安全传输,需手动分块、校验、防中断、加密;客户端切片上传,服务端暂存、拼接、md5校验、ttl清理;须用wss、aes-gcm加密、独立进程处理、断点续传及连接级状态管理。

Workerman 本身不内置文件传输协议,但能通过自定义 TCP/WebSocket 协议 + 分块 + 状态管理 + 加密,实现跨平台 IM 中的文件异步安全传输。关键不在“能不能”,而在“怎么切分、怎么校验、怎么防中断、怎么保私密”。
文件分块上传必须手动控制,不能依赖 HTTP 表单
客户端(Web/Android/iOS)若走 WebSocket 连接,onMessage 接收到的 $data 是原始二进制或 JSON 字符串,没有 $_FILES。直接传大文件会触发内存溢出或超时断连。
- 客户端需将文件按固定大小(如 64KB)切片,每片带唯一
file_id、chunk_index、total_chunks、md5(首片携带全文件 MD5) - 服务端用
tmp目录暂存分片,路径按file_id/chunk_index组织,避免命名冲突 - 收齐所有分片后,用
file_put_contents拼接,并校验最终文件 MD5 —— 不校验就写入,等于放弃完整性保障 - 未完成的
file_id目录需设 TTL(例如 2 小时),靠定时器清理,否则磁盘悄悄爆满
WebSocket 传二进制要显式设置 binaryType,且服务端必须识别
浏览器 WebSocket 默认 binaryType = 'blob',但 Workerman 的 onMessage 不自动解析 Blob;如果前端用 arraybuffer 发送,后端收到的是 string 类型的原始字节流,长度与 ArrayBuffer.length 一致,但内容是 raw bytes。
- 前端发送前务必设置:
ws.binaryType = 'arraybuffer',并用ws.send(new Uint8Array(buffer))或直接ws.send(buffer) - 服务端判断:
if (is_string($data) && strlen($data) > 0)才当作二进制处理;若混传 JSON 文本和二进制,必须加帧头标识类型(如首字节0x01表示文本,0x02表示分片) - 不要在同一个连接里交替发大文件和聊天消息——容易粘包,建议文件走独立连接或严格帧协议
安全不是加个 HTTPS 就完事:传输层加密 + 内容加密双保险
即使用了 ssl 配置启动 websocket://,也只是防中间人窃听;一旦服务器被入侵,临时分片、拼接后的文件仍明文可读。
- 服务端启动时必须配置 TLS:
new Worker('wss://0.0.0.0:8080', ['ssl' => [...]]),否则 iOS/Android WebView 会拒绝非安全 WebSocket 连接 - 敏感文件(如用户证件)应在客户端用 AES-GCM 加密后再分片上传,密钥由 IM 会话密钥派生(如 DH 协商后生成),服务端不接触明文密钥
- 服务端存储分片时,文件名不能含原始文件名(防路径遍历),推荐用
sprintf('%s_%d.bin', bin2hex(random_bytes(8)), $index) - 拼接完成后立即删原始分片目录,只保留加密后的最终文件(或转存到对象存储并删本地)
异步不是靠“多进程”实现的,而是靠连接隔离 + 回调驱动
Workerman 的 $worker->count = N 只控制进程数,不解决单连接内文件阻塞聊天消息的问题。真正异步,是指“一个连接传文件,不影响其他连接收发消息”,这靠的是事件循环天然隔离性,而非人工开线程。
- 不要在
onMessage里做sleep()、file_get_contents()大文件、或同步 DB 写入 —— 这些都会卡住整个进程的 event loop - 分片接收完成后的拼接和校验,建议投递到独立的
Worker进程(如FileProcessorWorker)处理,主 Worker 只管收包、转发、更新状态 - 客户端需实现断点续传:上传中途断连后,先发
{type: 'resume', file_id: 'xxx'},服务端返回已收分片索引列表,客户端跳过重传 - 每条文件传输任务应有独立
connection->file_upload_state属性记录进度,而不是用全局数组 —— 多连接并发时会错乱
Workerman 不会自动帮你恢复上下文。这部分逻辑必须由业务层兜底,框架只提供可靠的底层连接和事件回调。











