无尘阁日记

无尘阁日记

🛠 Yii2 文件上传遇到 $_FILES 为空的完整排查与解决方案
2025-07-21

在实际项目中,我们常常需要通过后端接口上传文件。最近我在使用 Yii2 框架中 yii\httpclient\Client 模拟上传文件请求时,发现 服务端的 $_FILES 始终为空,虽然请求日志中清楚显示 multipart 表单和文件内容都已发送。本文将完整记录问题排查和解决过程,供以后遇到类似问题时查阅参考。

🧩 场景描述

客户端代码(简化后的核心部分)

$request = Yii::$app->HttpClient->createRequest()    ->setUrl($url)    ->setMethod('POST')    ->addHeaders([        'Authorization' => 'Basic ' . $authToken,        'Content-Type' => 'multipart/form-data', // 👈 人为添加    ]);// 添加文件$request->addFile("attachmentForms[0].fdAttachment", $fileInfo['fdAttachment'], [    'fileName' => $fileInfo['fdFileName'],]);// 添加其他字段$request->addData([    'fdFlowId' => $params['fdFlowId'] ?? '',    'formValues' => json_encode($params['formValues'] ?? []),]);

服务端代码

public function actionReceiveTest(){    Yii::info('--- $_POST ---');    Yii::info(print_r($_POST, true));    Yii::info('--- $_FILES ---');    Yii::info(print_r($_FILES, true));    return $this->asJson([        'post' => $_POST,        'files' => $_FILES,    ]);}

实际现象

  • 请求日志中显示 multipart 请求结构完整,文件内容正确(二进制流存在)

  • $_POST 有内容,$_FILES 为空

  • 服务端收不到任何文件信息

🧪 分析与排查过程

✅ Step 1:客户端 Content-Type 设置有问题

'Content-Type' => 'multipart/form-data'

✅ 不要手动设置!
Yii2 的 yii\httpclient\Client 会自动生成带 boundary 的完整 multipart Content-Type 头。你手动设置会导致上传失败,$_FILES 解析失败。

✅ 正确做法:

$request->addHeaders([    'Authorization' => 'Basic ' . $authToken,    // 不要加 Content-Type]);

✅ Step 2:上传字段名使用了 PHP 无法识别的格式

你的字段名是:

"attachmentForms[0].fdAttachment"

这是非法字段,PHP 无法识别这种带.号的复杂结构,导致 $_FILES 解析不到任何内容。

✅ PHP 可接受的格式包括:

字段名格式解析方式
file$_FILES['file']
file[]多文件数组
form[file]多级结构
❌ form.file非法,不会出现在 $_FILES 中

✅ 正确做法:

$request->addFile("attachmentForms[0][fdAttachment]", $filePath, [    'fileName' => 'test.xlsx']);

或者更简单地直接使用字段名:

$request->addFile("file", $filePath, [    'fileName' => 'test.xlsx']);

✅ Step 3:服务端进一步验证建议

建议在服务端使用 Yii 提供的 UploadedFile 来获取上传文件,避免纯依赖 $_FILES

use yii\web\UploadedFile;$files = UploadedFile::getInstancesByName('file');Yii::info(print_r($files, true), __METHOD__);

✅ 最终正确版本(推荐用法)

🔧 客户端(发送文件)

$request = Yii::$app->HttpClient->createRequest()    ->setUrl($url)    ->setMethod('POST')    ->setOptions([        CURLOPT_TIMEOUT => 300,        CURLOPT_CONNECTTIMEOUT => 120,    ])    ->addHeaders([        'Authorization' => 'Basic ' . $authToken,    ]);// 添加文件$request->addFile('file', $filePath, [    'fileName' => 'upload.xlsx']);// 添加其他字段$request->addData([    'fdFlowId' => $params['fdFlowId'] ?? '',    'formValues' => json_encode($params['formValues'] ?? []),]);$res = $request->send();

🛠 服务端(接收文件)

public function actionReceiveTest(){    $uploadedFiles = \yii\web\UploadedFile::getInstancesByName('file');    return $this->asJson([        'code' => 0,        'post' => $_POST,        'files' => $_FILES,        'uploadedFiles' => array_map(function($file) {            return [                'name' => $file->name,                'size' => $file->size,                'tempName' => $file->tempName,                'type' => $file->type,            ];        }, $uploadedFiles),    ]);}

🔍 总结一张图

问题点是否会导致 $_FILES 为空解决方法
手动设置 Content-Type✅ 是删除,交给 Yii 自动生成
字段名中含 . 点号✅ 是改为 [] 或平铺结构
上传的不是文件路径,而是字符串✅ 是使用 fopen() 或确保是路径
服务端未开启上传支持✅ 是确认 php.ini 的 file_uploads=On

📘 推荐资源

✅ 结语

这个问题虽常见,但涉及 multipart 请求格式、HTTP 协议细节和 PHP 文件上传机制的组合,排查起来容易绕弯。通过本次总结,我们可以记住以下几个关键点:

  • ✅ 不要手动设置 multipart 的 Content-Type

  • ✅ 上传字段名要符合 PHP 的数组结构格式

  • ✅ 服务端优先使用 UploadedFile 类来读取上传文件

  • ✅ Curl 工具可以作为上传测试的有力辅助

希望这篇文章能为你在文件上传方面提供一份全面参考。

如夜话,至此。