Программирование, веб-кодинг, Расширения, виджеты

Загрузка изображений и файлов в Yii2. Их сохранение в папку на сервере и в БД. Ресайз изображений.

Добавим в форму редактирования поле для загрузки картинок к статье. Сохраним загруженные изображения в папку на сервере и запишем путь в БД. Покажем на примере ресайза с сохранением пропорций картинки обращаться к другим иногда необходимым методам Imagine.

Загрузка изображений и файлов в Yii2. Их сохранение в папку на сервере и в БД. Ресайз изображений.

Этот способ основан на подобном решении для Yii 1.1.x - см. http://loco.ru/materials/127-yii-image-upload

1. В форму редактирования статьи добавим/заменим поле для загрузки файла

<?php
    if(isset($model->image) && file_exists(Yii::getAlias('@webroot', $model->image)))
    { 
        echo Html::img($model->image, ['class'=>'img-responsive']);
        echo $form->field($model,'del_img')->checkBox(['class'=>'span-1']);
    }
?>
<?= $form->field($model, 'file')->fileInput() ?>

Не забудем добавить enctype в опциях вызова формы, раз собираемся загружать файлы через форму

$form = ActiveForm::begin(['id' => 'blog-form', 'options' => ['enctype' => 'multipart/form-data']]);

2. В модели добавим

use yii\web\UploadedFile;

ниже добавим переменные

class Blog extends \yii\db\ActiveRecord
{
    public $file;
    public $del_img;

в таблице БД поле для картинки у меня называется - image.

Далее в правилах rules() добавим

[['file'], 'file', 'extensions' => 'png, jpg'],
[['del_img'], 'boolean'],


3. В базовом контроллере добавим функцию проверки на существование папки и её создание в случае отсутствия (это нужно, чтобы складировать уменьшенные превью изображений отдельно в подпапки thumbs)

public function createDirectory($path) {   
    //$filename = "/folder/{$dirname}/";  
    if (file_exists($path)) {  
        //echo "The directory {$path} exists";  
    } else {  
        mkdir($path, 0775, true);  
        //echo "The directory {$path} was successfully created.";  
    }
}


4. Теперь в наш контроллер

use app\models\UploadForm;
use yii\web\UploadedFile;
use yii\imagine\Image;

Image - здесь это для создания превью главной фотки (а также для ресайза оригинала до разумных для веба размеров). Это расширение yii2-imagine от yiisoft - установите его через composer.

Привожу свой экшн actionCreate()

    public function actionCreate()
    {
        $model = new Blog();

        if ($model->load(Yii::$app->request->post())) {
            $file = UploadedFile::getInstance($model, 'file');
            if ($file && $file->tempName) {
                $model->file = $file;
                if ($model->validate(['file'])) {
                    
                    switch ($model->material_type) {
                        case 0:
                            $material_type = '';
                            break;
                        case 1:
                            $material_type = 'news/';
                            break;
                        case 2:
                            $material_type = 'persons/';
                            break;
                        case 3:
                            $material_type = 'movies/';
                            break;
                        case 4:
                            $material_type = 'interview/';
                            break;
                    }
                    
                    $dir = Yii::getAlias('images/blog/'.$material_type);
                    $fileName = $model->file->baseName . '.' . $model->file->extension;
                    $model->file->saveAs($dir . $fileName);
                    $model->file = $fileName; // без этого ошибка
                    $model->image = '/'.$dir . $fileName;
// Для ресайза фотки до 800x800px по большей стороне надо обращаться к функции Box() или widen, так как в обертках доступны только 5 простых функций: crop, frame, getImagine, setImagine, text, thumbnail, watermark
                    $photo = Image::getImagine()->open($dir . $fileName);
                    $photo->thumbnail(new Box(800, 800))->save($dir . $fileName, ['quality' => 90]);
                    //$imagineObj = new Imagine();
                    //$imageObj = $imagineObj->open(\Yii::$app->basePath . $dir . $fileName);
                    //$imageObj->resize($imageObj->getSize()->widen(400))->save(\Yii::$app->basePath . $dir . $fileName);
                    
                    Yii::$app->controller->createDirectory(Yii::getAlias('images/blog/'.$material_type.'/thumbs')); 
                    Image::thumbnail($dir . $fileName, 150, 70)
                    ->save(Yii::getAlias($dir .'thumbs/'. $fileName), ['quality' => 80]);
                }
            } 
            if ($model->save()) {
                return $this->redirect(['view', 'id' => $model->id]);
            }               
            
            
        } else {
            return $this->render('create', [
                'model' => $model,
            ]);
        }
    }

тут $material_type - для сохранения фоток по подпапкам.

экшн actionUpdate() почти такой же, как actionCreate():

    public function actionUpdate($id)
    {
        $model = $this->findModel($id);
        $current_image = $model->image;
        $model->sections = explode(',', $model->sections);

        if ($model->load(Yii::$app->request->post())) {
            
            $file = UploadedFile::getInstance($model, 'file');
            if ($file && $file->tempName) {
                $model->file = $file;
                if ($model->validate(['file'])) {
                    
                    //Если отмечен чекбокс «удалить файл»            
                    if($model->del_img)
                    {
                        if(file_exists(Yii::getAlias('@webroot'.$current_image)))
                        {
                            //удаляем файл
                            unlink(Yii::getAlias('@webroot'.$current_image));
                            $model->image = '';
                        }
                    }
                    
                    switch ($model->material_type) {
                        case 0:
                            $material_type = '';
                            break;
                        case 1:
                            $material_type = 'news/';
                            break;
                        case 2:
                            $material_type = 'persons/';
                            break;
                        case 3:
                            $material_type = 'movies/';
                            break;
                        case 4:
                            $material_type = 'interview/';
                            break;
                    }
                    
                    $dir = Yii::getAlias('images/blog/'.$material_type);
                    $fileName = $model->file->baseName . '.' . $model->file->extension;
                    $model->file->saveAs($dir . $fileName);
                    $model->file = $fileName; // без этого ошибка
                    $model->image = '/'.$dir . $fileName;
// Для ресайза фотки до 800x800px по большей стороне надо обращаться к функции Box() или widen, так как в обертках доступны только 5 простых функций: crop, frame, getImagine, setImagine, text, thumbnail, watermark
                    $photo = Image::getImagine()->open($dir . $fileName);
                    $photo->thumbnail(new Box(800, 800))->save($dir . $fileName, ['quality' => 90]);
                    //$imagineObj = new Imagine();
                    //$imageObj = $imagineObj->open(\Yii::$app->basePath . $dir . $fileName);
                    //$imageObj->resize($imageObj->getSize()->widen(400))->save(\Yii::$app->basePath . $dir . $fileName);
                    
                    Yii::$app->controller->createDirectory(Yii::getAlias('images/blog/'.$material_type.'/thumbs')); 
                    Image::thumbnail($dir . $fileName, 150, 70)
                    ->save(Yii::getAlias($dir .'thumbs/'. $fileName), ['quality' => 80]);
                }
            } 
            if ($model->save()) {
                return $this->redirect(['view', 'id' => $model->id]);
            }
            
        } else {
            return $this->render('update', [
                'model' => $model,
            ]);
        }
    }


Ресайз изображений при загрузке.

Чтобы изображения не сохранялись на сервере в большом размере и весе, мы сделали их ресайз. Для этого в контроллере вы видите обращение к getImagine. Метод Box(800, 800) позволит сохранять пропорции картинки. Чтобы к нему обратиться мы и подключили дополнительно Gd, Box, BoxInterface. Второй способ - закомментированные строки - использовать widen(800), чтобы при ресайзе задать ограничение по большей стороне картинки.

use Imagine\Gd;
use Imagine\Image\Box;
use Imagine\Image\BoxInterface;

...
                    ...
                    // Для ресайза фотки до 800x800px по большей стороне надо обращаться к функции Box() или widen, так как в обертках доступны только 5 простых функций: crop, frame, getImagine, setImagine, text, thumbnail, watermark
                    $photo = Image::getImagine()->open($dir . $fileName);
                    $photo->thumbnail(new Box(800, 800))->save($dir . $fileName, ['quality' => 90]);
                    //$imagineObj = new Imagine();
                    //$imageObj = $imagineObj->open(\Yii::$app->basePath . $dir . $fileName);
                    //$imageObj->resize($imageObj->getSize()->widen(800))->save(\Yii::$app->basePath . $dir . $fileName);