Каждый второй проект на Yii2 рано или поздно сталкивается с загрузкой файлов. Аватары, документы, изображения в галерею — без этого никуда. Но многие разработчики, особенно начинающие, делают это через раз, с копипастой из документации и велосипедами. Сегодня разберём, как правильно организовать файловое хранилище и загрузки в Yii2, чтобы не стрелять себе в ногу и не переписывать код через месяц.
Почему не стоит хранить файлы в базе данных
Сразу к делу: забудьте про BLOB-поля в БД для файлов. Это моветон и боль. База данных раздувается, бэкапы становятся тяжёлыми, а производительность падает. Файлы должны лежать в файловой системе, а в БД — только путь к ним.
Представьте, что ваш сайт — это склад. База данных — это учётная книга, а файлы — это коробки с товаром. Если вы кладёте коробки прямо в книгу, она развалится. Так и здесь: храните в БД только ссылку на коробку, а саму коробку — на полке.
В Yii2 для этого есть удобный компонент — FileStorage, который входит в состав расширения yii2-imagine или можно написать свой. Но чаще всего используют просто атрибут модели с типом file и методы saveAs(). Однако для серьёзных проектов лучше сразу настроить нормальное хранилище.
Настройка файлового хранилища в Yii2
Yii2 не имеет встроенного файлового хранилища, но есть отличное расширение — creocoder/yii2-flysystem, которое позволяет работать с локальной файловой системой, S3, FTP и другими через единый интерфейс. Установка простая:
composer require creocoder/yii2-flysystem
Затем настраиваем компонент в конфигурации приложения (обычно это файл config/main.php):
'components' => [
'fs' => [
'class' => 'creocoder\flysystem\LocalFilesystem',
'path' => '@webroot/uploads',
],
],
Теперь мы можем использовать компонент fs для работы с файлами. Например, чтобы сохранить файл:
$file = \Yii::$app->fs;
$file->write('path/to/file.txt', 'Содержимое файла');
А чтобы прочитать:
$content = $file->read('path/to/file.txt');
Всё просто и удобно. Но не забывайте, что для веб-доступа к файлам нужно настроить веб-сервер или использовать алиас @web/uploads.
Загрузка файлов через модели и формы
Теперь перейдём к самому интересному — загрузке файлов с помощью моделей и форм. В Yii2 это делается через валидатор file и метод upload().
Создадим модель загрузки файла:
namespace app\models;
use yii\base\Model;
use yii\web\UploadedFile;
class UploadForm extends Model
{
public $file;
public function rules()
{
return [
[['file'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg, pdf', 'maxSize' => 1024 * 1024 * 2],
];
}
public function upload()
{
if ($this->validate()) {
$this->file->saveAs('uploads/' . $this->file->baseName . '.' . $this->file->extension);
return true;
} else {
return false;
}
}
}
В контроллере обрабатываем загрузку:
public function actionUpload()
{
$model = new UploadForm();
if (\Yii::$app->request->isPost) {
$model->file = UploadedFile::getInstance($model, 'file');
if ($model->upload()) {
// файл успешно загружен
\Yii::$app->session->setFlash('success', 'Файл загружен!');
return $this->redirect(['view']);
}
}
return $this->render('upload', ['model' => $model]);
}
А во вьюхе делаем форму:
<?php
use yii\widgets\ActiveForm;
?>
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>
<?= $form->field($model, 'file')->fileInput() ?>
<button>Загрузить</button>
<?php ActiveForm::end() ?>
Обратите внимание на enctype — без него форма не отправит файл. Это типичная ошибка новичков.
Правильная организация хранилища и безопасность
Хранить все файлы в одной папке — плохая идея. Рано или поздно файлов станет много, и папка превратится в свалку. Лучше разбивать по подпапкам, например, по дате или по типу.
Также важно думать о безопасности. Никогда не доверяйте имени файла, которое отправляет пользователь. Злоумышленник может отправить имя с вредоносным расширением или с путями, типа ../../../etc/passwd. Поэтому всегда генерируйте новое имя файла сами, например, с помощью функции uniqid() или Yii::$app->security->generateRandomString().
Вот пример безопасной загрузки:
public function upload()
{
if ($this->validate()) {
$basePath = \Yii::getAlias('@webroot/uploads/');
$fileName = \Yii::$app->security->generateRandomString(10) . '.' . $this->file->extension;
$this->file->saveAs($basePath . $fileName);
$this->filePath = 'uploads/' . $fileName;
return true;
}
return false;
}
Кстати, проверяйте MIME-тип файла, а не только расширение. Расширение легко подделать. В Yii2 валидатор file имеет параметр mimeType — используйте его.
Скачивание файлов с контролем доступа
Иногда нужно отдавать файлы не напрямую, а через контроллер, чтобы проверить права пользователя. Например, если файл доступен только администраторам. В Yii2 это делается так:
public function actionDownload($id)
{
$model = File::findOne($id);
if (!$model) {
throw new \yii\web\NotFoundHttpException('Файл не найден');
}
// Проверяем права доступа
if (!\Yii::$app->user->can('downloadFile', ['model' => $model])) {
throw new \yii\web\ForbiddenHttpException('Доступ запрещён');
}
$filePath = \Yii::getAlias('@webroot/') . $model->path;
if (!is_file($filePath)) {
throw new \yii\web\NotFoundHttpException('Файл не существует');
}
return \Yii::$app->response->sendFile($filePath, $model->original_name);
}
Метод sendFile отправляет файл в браузер с правильными заголовками. Он автоматически обрабатывает большие файлы и поддерживает частичную загрузку (для докачки).
Работа с изображениями и их обработка
Часто нужно не просто сохранить картинку, но и создать превью или изменить размер. Для этого в Yii2 есть расширение yii2-imagine — обёртка над библиотекой Imagine.
composer require yiisoft/yii2-imagine
Пример использования:
use yii\imagine\Image;
// Создаём превью 100x100
Image::thumbnail('uploads/image.jpg', 100, 100)
->save('uploads/thumbs/image.jpg', ['quality' => 80]);
Также можно изменять размер с сохранением пропорций, добавлять водяные знаки и т.д. Всё это интуитивно понятно.
Обработка ошибок и отладка
При работе с файлами часто возникают ошибки: неверные права на папку, превышение лимита памяти, неверный путь. Вот несколько советов:
- Всегда проверяйте права на папку загрузки. Обычно нужны права 755 или 775.
- Если файл не загружается, проверьте upload_max_filesize и post_max_size в php.ini.
- Используйте Yii::$app->errorHandler, чтобы логировать ошибки загрузки.
- Включите режим отладки Yii2, чтобы видеть подробные сообщения об ошибках.
Для отладки загрузки файлов можно использовать следующий код в контроллере:
if ($model->validate()) {
// всё ок
} else {
var_dump($model->errors);
die;
}
Но не забывайте убирать такие вещи в продакшене.
Заключение
Работа с файлами в Yii2 — это не ракетостроение, но требует внимания к деталям. Используйте нормальное хранилище, переименовывайте файлы, проверяйте типы и не забывайте про права доступа. Тогда ваш код будет надёжным и безопасным.
Если вы хотите сэкономить время на разработке или вам нужна помощь с проектом на Yii2 — обращайтесь в студию WNDER. Мы поможем и с файлами, и с более сложными задачами.



