Рано или поздно каждый разработчик сталкивается с необходимостью загружать файлы на сервер. Это может быть аватарка пользователя, документ PDF или галерея изображений. В Yii2 есть всё необходимое для комфортной работы с файлами, но есть и подводные камни. В этой статье разберём, как организовать загрузку и хранение файлов в Yii2, чтобы потом не пришлось краснеть перед заказчиком.
Зачем вообще нужна отдельная логика для файлов?
Многие новички думают: «Что сложного? Взял файл из формы, сохранил в папку — и готово». Но в реальных проектах всё сложнее. Нужно проверить тип файла, ограничить размер, защититься от вредоносных скриптов, придумать уникальное имя, чтобы не перезаписать чужие данные, и позаботиться о том, чтобы файлы не потерялись при масштабировании.
В Yii2 загрузка файлов построена вокруг класса yii\web\UploadedFile. Он берёт на себя часть рутины: валидацию, сохранение, обработку ошибок. Но чтобы всё работало как часы, нужно правильно настроить модель и контроллер.
Простейшая загрузка: от формы до сервера
Начнём с классики — загрузки одного файла через форму. Допустим, мы делаем загрузку аватарки для пользователя.
Сначала создадим форму в представлении. Важно не забыть указать enctype="multipart/form-data", иначе файл не передастся.
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
$form = ActiveForm::begin([
'options' => ['enctype' => 'multipart/form-data'],
]); ?>
<?= $form->field($model, 'avatar')->fileInput() ?>
<div class="form-group">
<?= Html::submitButton('Загрузить', ['class' => 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
Теперь модель. Здесь мы объявляем свойство $avatar и правило валидации для него.
namespace app\models;
use yii\base\Model;
class UploadForm extends Model
{
public $avatar;
public function rules()
{
return [
[['avatar'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg, jpeg', 'maxSize' => 1024 * 1024 * 2],
];
}
}
В контроллере обрабатываем форму: если валидация прошла, сохраняем файл.
public function actionUpload()
{
$model = new UploadForm();
if (Yii::$app->request->isPost) {
$model->avatar = UploadedFile::getInstance($model, 'avatar');
if ($model->validate()) {
$path = Yii::getAlias('@webroot/uploads/') . $model->avatar->baseName . '.' . $model->avatar->extension;
if ($model->avatar->saveAs($path)) {
Yii::$app->session->setFlash('success', 'Файл загружен');
return $this->redirect(['site/index']);
}
}
}
return $this->render('upload', ['model' => $model]);
}
Это базовый сценарий. Но обратите внимание: мы используем оригинальное имя файла. Если два пользователя загрузят файлы с одинаковым именем, второй перезапишет первый. Чтобы избежать этого, нужно генерировать уникальное имя.
Yii::$app->security->generateRandomString(16) . '.' . $file->extension.
Валидация файлов: что проверять и как не пропустить угрозу
Валидация — это ваш щит. Без неё на сервер может попасть что угодно: от гигабайтного видео до PHP-скрипта, замаскированного под картинку. В Yii2 валидатор file умеет проверять расширение, MIME-тип и размер. Но есть нюансы.
Стандартные правила выглядят так:
return [
[['avatar'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxSize' => 1024 * 1024 * 2, 'checkExtensionByMimeType' => true],
];
checkExtensionByMimeType — важный параметр. Если он включён (по умолчанию), Yii2 сверяет расширение файла с его MIME-типом. Это защищает от подмены: например, если вы разрешили только PNG, а пользователь загрузил PHP-скрипт с расширением .png, валидатор это заметит и отклонит.
Однако не полагайтесь только на расширение. MIME-тип тоже можно подделать, но это сложнее. Для максимальной безопасности используйте дополнительную проверку через функцию finfo_file() или библиотеку для работы с изображениями (например, getimagesize()).
Вот таблица с основными параметрами валидатора file:
| Параметр | Описание | Пример |
|---|---|---|
| extensions | Список разрешённых расширений | 'png, jpg, gif' |
| maxSize | Максимальный размер в байтах | 1024 * 1024 * 2 (2 МБ) |
| checkExtensionByMimeType | Сверять ли расширение с MIME-типом | true (по умолчанию) |
| mimeTypes | Разрешённые MIME-типы (можно ограничить) | 'image/png, image/jpeg' |
Помимо валидатора, всегда проверяйте, что файл действительно загружен через HTTP POST. В Yii2 это делает UploadedFile::getInstance() — он возвращает null, если файл не был загружен.
Хранение файлов: папки, база данных или облако?
Когда файл загружен, его нужно где-то хранить. Самый простой вариант — локальная файловая система. Но и здесь есть простор для творчества.
Структура папок. Не сваливайте все файлы в одну кучу. Если у вас тысячи изображений, папка с ними станет неповоротливой. Разбивайте по датам или по ID пользователя. Например: /uploads/2025/04/15/abc123.jpg. Это упростит поиск и резервное копирование.
Имена файлов. Как мы уже говорили, генерируйте уникальные имена. Можно использовать хеш от содержимого файла (например, md5_file()) — это гарантирует уникальность и позволит избежать дубликатов. Но учтите, что хеш от большого файла вычисляется не мгновенно.
Права доступа. Папка для загрузок должна быть доступна для записи веб-серверу, но не должна позволять выполнение скриптов. Если вы используете Apache, добавьте в .htaccess в папке загрузок строку php_flag engine off или запретите выполнение PHP через <FilesMatch \.php$> Deny from all </FilesMatch>.
База данных или файловая система? Иногда спрашивают: может, хранить файлы в БД (BLOB)? Технически можно, но не рекомендуется для больших объёмов. Файловая система быстрее, проще масштабируется и не раздувает базу. БД лучше использовать для метаданных: имя файла, размер, MIME-тип, путь.
Вот сравнение подходов к хранению:
| Подход | Плюсы | Минусы |
|---|---|---|
| Локальная ФС | Простота, скорость, дешевизна | Сложно масштабировать, нужен бэкап |
| База данных (BLOB) | Целостность, транзакции | Раздувание БД, медленный доступ |
| Облако (S3, CDN) | Масштабируемость, отказоустойчивость | Стоимость, зависимость от интернета |
Для большинства проектов на Yii2 достаточно локальной ФС с разумной структурой. Если проект растёт, можно подключить облачное хранилище через расширения (например, yii2-aws-s3).
Множественная загрузка и работа с несколькими файлами
Часто нужно загрузить не один файл, а несколько — например, фотографии товара. В Yii2 это делается с помощью UploadedFile::getInstances().
В представлении используйте fileInput(['multiple' => true]). В модели объявите массив:
public $gallery;
public function rules()
{
return [
[['gallery'], 'file', 'skipOnEmpty' => true, 'extensions' => 'png, jpg', 'maxFiles' => 5],
];
}
В контроллере получите все файлы и обработайте каждый в цикле:
$model->gallery = UploadedFile::getInstances($model, 'gallery');
if ($model->validate()) {
foreach ($model->gallery as $file) {
$filename = Yii::$app->security->generateRandomString(16) . '.' . $file->extension;
$file->saveAs(Yii::getAlias('@webroot/uploads/') . $filename);
}
}
Не забудьте про ограничение maxFiles, чтобы пользователь не загрузил сотню файлов и не положил сервер.
Итог: что запомнить
Работа с файлами в Yii2 — это не магия, а последовательность простых шагов. Главное — не лениться и делать всё по правилам:
- Всегда используйте
UploadedFileи валидаторfile. - Генерируйте уникальные имена файлов.
- Проверяйте MIME-тип и расширение.
- Организуйте папки в понятную структуру.
- Запретите выполнение скриптов в папке загрузок.
Следуя этим советам, вы избежите большинства проблем с безопасностью и потерянными файлами. А если проект вырастет, вы будете готовы к переходу на облачное хранилище.
Удачи в разработке!


