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