Yii2文件上传需分场景处理:单图须设'skipOnEmpty'=>false并校验;多图用getInstances()或FileInput组件;远程URL需FileFromUrlValidator校验;安全须三重校验扩展名、MIME及文件头魔数;目录自动创建需mkdir($path,0755,true)。

你需要在Yii2项目中处理用户上传的文件,但面对单图、多图、远程URL、安全校验、目录自动创建等不同场景时,不知道该用哪个类、怎么配规则、哪里容易踩坑。
单文件上传:从模型定义到保存落地
第一步:在models目录下新建UploadForm.php,声明$file属性并绑定file验证器。
第二步:rules()方法中必须写明'skipOnEmpty' => false,否则空文件提交会跳过验证直接走upload()逻辑,导致saveAs()报错。
第三步:控制器里调用UploadedFile::getInstance($model, 'file')获取实例,【注意:参数名必须和模型属性名完全一致,大小写敏感】。
第四步:执行$model->upload()前先判断if ($model->validate()),验证失败时不能硬存——否则可能把未通过扩展名校验的.php文件写入uploads目录。
多文件上传:一次选10张图的实操配置
方法一:用Yii原生UploadedFile::getInstances()(推荐)
① 模型rules()中设置'maxFiles' => 10,并确保extensions允许jpg,png,gif等实际需要的类型;
② 视图中使用=$form->field($model, 'file[]')->fileInput(['multiple' => true])?>,name属性带[]才能被PHP识别为数组;
③ 控制器里用$files = UploadedFile::getInstances($model, 'file')拿到对象数组,foreach遍历时逐个调用$fl->saveAs();
④ mkdir()创建深层目录时,第三个参数必须设为true,否则像uploads/20260806/这种两级路径会创建失败。
方法二:改用kartik\widgets\FileInput组件(适合带预览/拖拽需求)
安装后直接在视图中写echo $form->field($model,'image[]')->widget(FileInput::classname(),['options'=>['multiple'=>true]]);,它自动处理前端多选+后端接收逻辑。
远程图片上传:粘贴链接自动下载保存
先用composer require igogo5yo/yii2-upload-from-url装扩展,再按三步走:
第一步:在模型rules()里加FileFromUrlValidator验证器,指定'extensions' => 'jpg,png,gif'和'mimeTypes' => 'image/*',防止用户填txt或exe链接;
第二步:控制器中用UploadFromUrl::initWithModel($model, 'image')初始化对象,【此时$model->image必须已赋值为合法URL字符串】;
第三步:调用$file->saveAs('uploads/remote_' . time() . '.' . $file->getExtension(), true),第二个参数true表示自动把保存路径回写到$model->image属性。
安全防护:绕过前端限制的后端兜底策略
所有上传入口必须强制校验三件事:文件扩展名、MIME类型、文件头魔数。
扩展名校验靠file规则的'extensions'参数,但仅此不够——攻击者可改后缀骗过;
MIME类型校验要加'mimeTypes' => ['image/jpeg', 'image/png'],且需配合enableStrictParsing设为true;
最可靠的是读取文件头做魔数比对,例如jpeg开头必为\xFF\xD8\xFF,这个逻辑要写在upload()方法里,用fopen+bindec手动检测;
上传目录权限必须设为0755,绝不能是0777,否则配合解析漏洞可能被写入webshell。
自动分目录存储:避免 uploads 文件夹爆炸
用date("YmdH", time())生成小时级子目录,比如uploads/2026080612/;
mkdir($path, 0755, true)第三个参数true不可省略,否则深层路径创建失败;
保存文件名建议用uniqid() . '_' . $file->baseName . '.' . $file->extension,避免中文名乱码和重名覆盖;
如果项目部署在Windows服务器上,路径分隔符要用DIRECTORY_SEPARATOR代替硬编码的/,否则mkdir会报错。


















