You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Plupload 2.3.6分块视频上传完成后无法存入目标目录

问题现象

前端可正常展示文件或文件分块的上传进度,进度达到100%后提示上传完成,但上传结束后,无论配置的目标目录还是项目其他目录下,都找不到已上传的文件,Plupload 2.3.6版本、3.x版本均存在该问题。


核心问题原因

Plupload的上传进度是根据客户端已发送的字节数计算的,只要前端把文件全部分片发送完成就会显示100%进度,不会校验后端是否真正处理、保存成功。你的后端PHP代码存在多处语法、逻辑错误,且所有错误都被@错误抑制符掩盖,导致脚本运行失败但前端无感知。

具体错误点如下:

  • 目标目录路径配置错误
    代码中配置的$targetDir = '/uploads';是操作系统根目录下的绝对路径,并非网站项目内的uploads目录。绝大多数Web服务运行用户(如www-data、nginx)没有系统根目录的写入权限,文件根本无法写入;就算写入成功,在项目目录下也无法找到对应文件。
  • 存在两处PHP语法致命错误
    1. 获取文件名逻辑中,$fileName = $_REQUEST['name'] 行尾缺少分号,会直接触发PHP语法解析错误,脚本中断运行
    2. 清理临时文件逻辑中,将PHP内置函数filemtime()错写为不存在的filetime(),触发致命错误导致脚本中断
  • 上传合法性校验参数错误
    校验上传文件合法性的逻辑中,is_uploaded_file()要求传入服务器端临时文件路径$_FILES["file"]["tmp_name"],但代码中传入的是客户端上传的原始文件名$_FILES["file"]["name"],该校验会永远判定为非法上传,直接返回错误。
  • 目录创建逻辑缺陷
    代码用@mkdir($targetDir);创建目录时,未指定目录权限、未开启递归创建参数,就算路径配置正确,默认创建的目录也可能因权限不足导致Web用户无法写入文件。

修复方案
  1. 修正目标目录路径,使用PHP魔术常量__DIR__获取当前脚本所在目录,拼接成项目内的绝对路径,比如$targetDir = __DIR__ . '/uploads';(确保upload.php文件和uploads目录在同级)
  2. 补上缺失的分号,将错误的filetime()修正为filemtime()
  3. 修正is_uploaded_file()的传入参数为$_FILES["file"]["tmp_name"]
  4. 修改目录创建逻辑,指定目录权限为0755,开启递归创建,去掉错误抑制符,方便排查问题:
if (!file_exists($targetDir)) {
    mkdir($targetDir, 0755, true);
}
  1. 排查阶段暂时去掉代码中所有的@错误抑制符,打开PHP错误提示,方便定位运行时问题。

修正后的PHP核心代码参考:

$targetDir = __DIR__ . '/uploads';
$cleanupTargetDir = true;
$maxFileAge = 5 * 3600;

// 创建目标目录
if (!file_exists($targetDir)) {
    mkdir($targetDir, 0755, true);
}

// 获取文件名
if (isset($_REQUEST['name'])) {
    $fileName = $_REQUEST['name'];
}
elseif (!empty($_FILES)) {
    $fileName = $_FILES["file"]["name"];
}
else {
    $fileName = uniqid("file_");
}

$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;

$chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
$chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;

// 清理旧临时文件
if ($cleanupTargetDir) {
    if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {
        die('{"jsonrpc" : "2.0", "error" : {"code" : 100, "message" : "Failed to open temporary directory."}, "id" : "id"}');
    }

    while (($file = readdir($dir)) !== false) {
        $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
        if ($tmpfilePath == "{$filePath}.part") {
            continue;
        }
        // 修正函数名
        if (preg_match('/\.part$/', $file) && (filemtime($tmpfilePath) < time() - $maxFileAge)) {
            unlink($tmpfilePath);
        }
    }
    closedir($dir);
}

// 打开临时输出文件
if (!$out = fopen("{$filePath}.part", $chunks ? "ab" : "wb")) {
    die('{"jsonrpc" : "2.0", "error" : {"code" : 102, "message" : "Failed to open output stream."}, "id" : "id"}');
}

if (!empty($_FILES)) {
    // 修正is_uploaded_file参数
    if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
        die('{"jsonrpc" : "2.0", "error" : {"code" : 103, "message" : "Failed to move uploaded file."}, "id" : "id"}');
    }

    if (!$in = fopen($_FILES["file"]["tmp_name"], "rb")) {
        die('{"jsonrpc" : "2.0", "error" : {"code" : 101, "message" : "Failed to open input stream."}, "id" : "id"}');
    }
}
else {
    if (!$in = fopen("php://input", "rb")) {
        die('{"jsonrpc" : "2.0", "error" : {"code" : 101, "message" : "Failed to open input stream."}, "id" : "id"}');
    }
}

while ($buff = fread($in, 4096)) {
    fwrite($out, $buff);
}

fclose($out);
fclose($in);

// 所有分片上传完成后重命名临时文件
if (!$chunks || $chunk == $chunks - 1) {
    rename("{$filePath}.part", $filePath);
    die('{"jsonrpc" : "2.0", "result" : {"status" : 200, "message" : "The file has been uploaded, successfully."}}');
}

验证步骤
  • 手动在项目对应路径创建uploads目录,设置目录权限为0755,确保Web服务运行用户有读写权限
  • 替换修正后的代码重新上传,上传完成后到__DIR__ . '/uploads'对应的路径下即可找到上传的文件。

内容的提问来源于stack exchange,提问作者Yemi A.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 10:18:29