🛠 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 工具可以作为上传测试的有力辅助
希望这篇文章能为你在文件上传方面提供一份全面参考。
如夜话,至此。
发表评论: