Workerman 5.1+:超过 2048 字节的 multipart 请求未自动清理上传临时文件

final507

Workerman 5.1+:超过 2048 字节的 multipart 请求未自动清理上传临时文件

问题描述

在 Webman 项目中,通过 multipart/form-data 上传文件时,Workerman 会将上传文件写入临时目录。

测试发现:当完整 HTTP 请求包长度超过 2048 字节时,请求处理结束后不会自动调用 $request->destroy(),导致未被业务逻辑移动或删除的上传临时文件持续保留在临时目录中。

其中,2048 字节是 Workerman TcpConnection::MAX_CACHE_STRING_LENGTH 的请求缓存阈值,判断对象是完整 HTTP 请求原始数据长度,并非单纯的上传文件大小。

对于请求包长度不超过 2048 字节的单请求,Workerman 在回调结束后会调用 destroy();但当请求超过该阈值时,请求不会进入该缓存分支,onMessage 返回后也没有对应的 destroy() 清理逻辑。

Request::destroy() 会删除上传文件信息中 tmp_name 对应的临时文件。因此,未执行该方法可能导致上传临时目录持续堆积文件。

重现步骤

  1. 配置 Workerman 上传临时目录:
use Workerman\Protocols\Http;

Http::uploadTmpDir(runtime_path('upload-tmp'));
  1. 发起一个 multipart/form-data 的 POST 请求,同时携带普通请求参数和上传文件。
  2. 保证完整 HTTP 请求包长度超过 2048 字节。例如上传一个大于 2KB 的文件(实际阈值还会受到请求头、boundary、字段名和普通参数长度影响)。
  3. 控制器中不调用 $file->move(),或不主动删除上传临时文件。
  4. 请求完成后检查 runtime/upload-tmp 目录。

结果

上传临时文件仍然存在,未被自动清理。

相关逻辑

Workerman 在 TcpConnection 中对完整请求包长度不超过 MAX_CACHE_STRING_LENGTH(2048)的请求进入缓存分支,并在执行完 onMessage 后调用:

$request->destroy();

当请求包长度超过 2048 字节时,不会进入该缓存分支;请求执行完 onMessage 后没有对应的 destroy() 调用。

临时修复方案

通过继承并重载 Webman 的 App 类,在每次请求处理结束后统一调用 destroy():

<?php

namespace app\admin\process;

use Webman\App;
use Workerman\Protocols\Http\Request as WorkermanRequest;

/**
 * 自定义 HTTP 应用,确保请求结束后清理上传临时文件。
 */
class Http extends App
{
    /**
     * 请求结束后清理 Workerman 上传临时文件。
     *
     * @param mixed $connection
     * @param mixed $request
     * @return null
     */
    public function onMessage($connection, $request)
    {
        try {
            return parent::onMessage($connection, $request);
        } finally {
            if ($request instanceof WorkermanRequest) {
                $request->destroy();
            }
        }
    }
}

并在 config/process.php 中使用自定义的 HTTP 应用类。

环境信息

  • 操作系统:Windows 10
  • PHP:>= 8.1
  • workerman/webman-framework:^2.1
  • workerman/workerman:>= 5.1
191 1 0
1个回答

walkor 打赏

这个现象只在 5.1.0 到 5.1.4 上成立。
你装的不是最新版。这个问题在 Workerman 5.1.5 已经修好了,请升级到 5.1.5 或更高版本(当前是 5.2.3)

🔝