Загрузка картинок с преобразованием размера в Yii через форму. Продолжение.

Загрузка картинок с преобразованием размера в Yii через форму. Продолжение. Делаем ресайз картинок, из одной картинки делаем несколько картинок разного размера и сохраняем их по разным папкам. Отображаем нужные картинки в соответствующих местах.

В предыдушей статье про загрузку файлов изображений в Yii к материалу мы немножно слукавили, то есть умолчали о некоторых недостатках. Тогда целью было сделать удобную возможность загрузки картинок с автоматическим переименовыванием файла картинки, а также удаление картинки (из поля базы и с сервера). И эта цель успешно была достигнута. Но мы не ресайзили картинку, в то время как отображается она только как превьюшка шириной 150px. А вдруг сама картинка - 1000 или 2000 px? Мы поставили в правилах только ограничение на загрузку картинки по весу - до 5 Mb.

Сегодня дорабатываем наш алгоритм загрузки картинкок в Yii:

  • Научим картинку сохраняться в 3 вариантах - маленькая (50px по длинной стороне), превьюшка (шириной 200px), большая (800px по длинной стороне);
  • Оставляем нерешённой на следующий раз ошибку с отсутствием папки для сохранения (нужно просто дописать проверку существует ли такая папка и если не существует, то создать её);
  • Перепишем функцию показа картинки - будет картинкой-превью ссылкой на подробную новость, либо большой картинкой в новости (не ссылка!!)
  • Добавим колонку картинок (маленьких, тех что уменьшатся до 50px) в интерфейсе администратора, то есть в списке, выводимом с помощью CGridView в экшне admin контроллера Post (Post.admin)


Изменим функции actionCreate и actionUpdate нашего контроллера Post.
actionCreate:
public function actionCreate()
	{
		$model=new Post;
		// Uncomment the following line if AJAX validation is needed
		// $this->performAjaxValidation($model);
		if(isset($_POST['Post'])) //Если пользователь отослал данные
		{
			$model->attributes=$_POST['Post']; //Заполнить модель данными присланными пользователем
			$model->icon=CUploadedFile::getInstance($model,'icon'); //Атрибуту icon присвоить указатель на загружаемый файл
			if ($model->icon){
				$sourcePath = pathinfo($model->icon->getName());	
				$fileName = substr($model->time_start, 5, 5).'-'.$model->alias.'.'.$sourcePath['extension'];
				$model->image = $fileName;
			}
			if($model->save()){
				//Если поле загрузки файла не было пустым, то            
				if ($model->icon){				
					//сохранить файл на сервере в каталог images/2011 под именем 
					//month-day-alias.jpg
					$file = $_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/'.$fileName; //Переменной $file присвоить путь, куда сохранится картинка без изменений
					$model->icon->saveAs($file);
					
					//Используем функции расширения CImageHandler;
					$ih = new CImageHandler(); //Инициализация
					Yii::app()->ih 
					->load($file) //Загрузка оригинала картинки
					->thumb('200', false) //Создание превьюшки шириной 200px
					->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/thumbs/'.$fileName) //Сохранение превьюшки в папку thumbs
					->reload() //Снова загрузка оригинала картинки
					->thumb('50', '50') //Создание превьюшки размером 50px
					->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/thumbs_small/'.$fileName) //Сохранение превьюшки в папку thumbs_small
					->reload()//Снова загрузка оригинала картинки
					->thumb('800', '800') //Создание превьюшки размером 800px
					->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/'.$fileName) //Сохранение большой картинки в папку
					;
				}
				$this->redirect(array('view','id'=>$model->id));
			}
		}                $this->render('create',array(
			'model'=>$model,
		));
	}
actionUpdate:
public function actionUpdate()
	{
		$model=$this->loadModel();
		if(isset($_POST['Post']))
		{
			$model->attributes=$_POST['Post'];
			
			$model->icon=CUploadedFile::getInstance($model,'icon');
			if ($model->icon){
				$sourcePath = pathinfo($model->icon->getName());
				$fileName = substr($model->time_start, 5, 5).'-'.$model->alias.'.'.$sourcePath['extension'];
				$model->image = $fileName;
			}
			if($model->save()){            
				if($model->del_img) //Если отмечен чекбокс «удалить файл»
				{
					if(file_exists($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/'.$model->image))
					{
						//удаляем картинку и все её thumbs
						@unlink('./images/'.substr($model->time_start, 0, 4).'/'.$model->image);
						@unlink('./images/'.substr($model->time_start, 0, 4).'/thumbs/'.$model->image);
						@unlink('./images/'.substr($model->time_start, 0, 4).'/thumbs_small/'.$model->image);						
						$model->image = '';
        			}
        		}
        		
				//Если поле загрузки файла не было пустым, то            
				if ($model->icon){
					$file = './images/'.mb_substr($model->time_start, 0, 4, 'UTF-8').'/'.$fileName;
					$model->icon->saveAs($file); //сохранить файл на сервере под именем 12-03-alias.jpg Если файл с таким именем существует, он будет заменен.
					
					//Как в actionCreate - используем функции расширения CImageHandler;
					$ih = new CImageHandler(); //Инициализация
					Yii::app()->ih 
					->load($file) //Загрузка оригинала картинки
					->thumb('200', false) //Создание превьюшки шириной 200px
					->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/thumbs/'.$fileName) //Сохранение превьюшки в папку thumbs
					->reload() //Снова загрузка оригинала картинки
					->thumb('50', '50') //Создание превьюшки размером 50px
					->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/thumbs_small/'.$fileName) //Сохранение превьюшки в папку thumbs_small
					->reload()//Снова загрузка оригинала картинки
					->thumb('800', '800') //Создание превьюшки размером 800px
					->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/'.$fileName) //Сохранение большой картинки в папку
					;
				}
				$this->redirect(array('view','id'=>$model->id));
			}
		}

		$this->render('update',array(
			'model'=>$model,
		));
	}

Алгоритм следующий:
Картинка загружается на сервер (это мы сделали в прошлый раз), потом она уменьшается до 200px по любой стороне и сохраняется в папку thumbs. Затем оригинал снова уменьшаемся до 50px и сохраняется в thumbs_small. Затем сам оригинал уменьшается до 800px и перезаписывает сам себя.

Ресайз делается с помощью расширения CImageHandler
Скачайте файл CImageHandler.php в папку  /protected/components. В файл /protected/config/main.php добавляем строку «ih»
// application components
	'components'=>array(
		'ih'=>array('class'=>'CImageHandler'),
		'user'=>array(...

Теперь можно добавлять картинку (в файлах видов изменения внесём ниже, когда поменяем функцию вывода картинки).
Только надо созжать необходимые папки, у нас внутри images - папки по годам (2011,2012), где сохраняются большие картинки, а в каждой из папок-годов - созданы папки thumbs и thumbs_small.

Вторая часть - добавим отображение картинок в admin и допишем функцию показа картинок.
В файле /protected/views/post/admin.php в CGridView вставляем колонку с картинками (размером 50px).

<?php
$this->breadcrumbs=array(
	'Manage Posts',
);
?>
<h1>Manage Posts</h1>

<?php $this->widget('zii.widgets.grid.CGridView', array(
	'id'=>'posts-grid',
	'dataProvider'=>$model->search(),
	'filter'=>$model,
	'columns'=>array(
		array(
			'name'=>'id',
			'headerHtmlOptions'=>array('width'=>'50px')
		),
		array(
			'name'=>'title',
			'type'=>'raw',
			'value'=>'CHtml::link(CHtml::encode($data->title), $data->url)'
		),
		array(
			'name'=>'image',
			'type'=>'image',
			//если файла картинки нет, 
			// то отображается файл noeventimage.png
			// Значение value обрабатывается функцией eval() поэтому 
			// одинарные ковычки.
			'value'=> 'file_exists($_SERVER["DOCUMENT_ROOT"].Yii::app()->urlManager->baseUrl."/images/".substr($data->time_start, 0, 4)."/thumbs_small/".$data->image) ? 
			Yii::app()->urlManager->baseUrl."/images/".substr($data->time_start, 0, 4)."/thumbs_small/".$data->image : 
			Yii::app()->urlManager->baseUrl."/css/noeventimage.png"',
			'filter'=>'',
			'headerHtmlOptions'=>array('width'=>'54px'),
		),
		...		array(
			'name'=>'status',
			'value'=>'Lookup::item("PostStatus",$data->status)',
			'filter'=>Lookup::items('PostStatus'),
		),
		array(
			'name'=>'create_time',
			//'type'=>'datetime',
			'filter'=>false,
		),
		array(
			'class'=>'CButtonColumn',
		),
	),
)); ?>
Колонка с картинкой имеет тип «image» В поле value должен приходить url картинки. При использовании типа image, нет возможности передавать значение alt, width или другие атрибуты тэгу «image». В админке это и не нужно.

На сайте же нужет тэг image обёрнутый ссылкой на странице списка, и не обёрнутый ссылкой в подробном описании. Изменяем функцию event_image() Находится она в файле контроллера /protected/controllers/PostController.php

public function event_image($id, $create_year, $alias, $title, $image, $class='material_img',$link=null,$target='_self')
	{
		if(isset($image) && file_exists($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.$create_year.'/'.$image))
		{
			if ($link)	
			return CHTML::link(
				CHtml::image(Yii::app()->getBaseUrl(true).'/images/'.$create_year.'/thumbs/'.$image, $title,
					array(
						'class'=>$class,
					)
				),
				$link,
				array('target'=>$target,)
			);
			
			else
				return CHtml::image(Yii::app()->getBaseUrl(true).'/images/'.$create_year.'/'.$image, $title,
			array(
				'class'=>$class,
			));
		}
		else
			return CHtml::image(Yii::app()->urlManager->baseUrl.'/css/noeventimage.png','Нет картинки',
			array(
				'class'=>$class
			));
	}
Эта функция возвращает картинку, или картинку, обёрнутую ссылкой. Или noeventimage.jpg, если нет изображения.
Используется эта функция в отображении /protected/views/post/_view.php (выводит картинку-ссылку)

<div><p><?php echo $this->event_image($data->id, substr($data->time_start, 0, 4), $data->alias, $data->title, $data->image, 'small_img left', $data->url);?></p></div>

И в отображении /protected/views/post/_view_full.php (выводит большую картинку)
<div><?php echo $this->event_image($data->id, mb_substr($data->time_start, 0, 4, 'UTF-8'), $data->alias, $data->title, $data->image);?></div>

Можно и думаю потребуется написать дополнительные функции конкретно под приложение, наша функция служит частным примером.


Источник: loco.ru

almix
Разработчик Loco, автор статей по веб-разработке на Yii, CodeIgniter, MODx и прочих инструментах. Создатель Team Sense.

Вы можете почитать все статьи от almix'а.



Другие статьи по этой теме:

Комментарии (5)     Подпишитесь на RSS комментариев к этой статье.

5 комментариев

#162
Илья говорит:
January 9, 2012 at 03:04 pm
добрый день, возникло недопонимание. послучается, что, для того чот бы при редактировании записи, что бы изменить изображение, нужно отметитьь чекбокс, и указать ссылку на новое изображение. но судя по скрипту мы сначало заносим имя новой переменной в $model->image , потом сохраняем все в базу, если успешно сохранилось, удаляем //удаляем картинку и все её thumbs
@unlink('./images/'.substr($model->time_start, 0, 4).'/'.$model->image); тоесть удаляем только что сохраненную в базу картинку, еще до ее загрузки, а старое изображение остается прежним. и честно говоря не увидел где мы проверяем на существование каталога для сохранения файлов.
#165
Саша говорит:
January 10, 2012 at 05:03 pm
Посмотрите первую часть про загрузку картинки - http://loco.ru/materials/127-yii-image-upload, там я пробами и ошибками пришёл к выводу, что надо до сохранения сделать имя картинки. Из поля image в базе данных видимо не происходит удаление имени каринки, вы это думаете?
#635
Сергей говорит:
September 10, 2012 at 03:47 pm

Вместо:

$_SERVER["DOCUMENT_ROOT"].Yii::app()->urlManager->baseUrl."/images/"

правильнее использовать:

Yii::getPathOfAlias('images.uploads');



#637
almix говорит:
September 11, 2012 at 03:27 pm
Сергей, расскажите почему ваш вариант правильнее? 
#685
Александр говорит:
October 18, 2012 at 01:01 pm
Почитайте про алиасы, и поймете почему Сергей прав.