Как стать автором
Обновить

Yii, пишим легкий widget и behavior для загрузки изображений

В одном из своих проектов, реализуемых на Yii, потребовалось сделать загрузку изображений, причем не хотелось использовать Flash и загрузку файлов через скрытый iframe. Задумавшись что мне не нужна поддержка ie меньше 10 версии я решил почему бы не использовать все прелести HTML5.

Немного теории


HTML5 предоставляет нам прекрасную возможность использовать загрузку изображений стандартными средствами используя File Api и работать с файлами через функцию FileReaderна стороне браузера.
Загрузив и считав изображение на клиенте, через свойство result функции FileReader.readAsDataURL получаем данные о файле в виде схемы data:URL — тоесть base64. Который в дальнейшем можем послать на сервер через ajax вместе с остальными полями формы, или послать форму через обычный Post и принять файл на сервере привычным образом через $_FILES

Реализация


Небольшой фрагмент кода метода run виджета.
#/extensions/ImageUploadInputWidget/ImageUploadInputWidget.php
	public function run()
	{
		list($name, $id) = $this->resolveNameID();

		if (isset($this->htmlOptions['id'])) {
			$id = $this->htmlOptions['id'];
		}
		else {
			$this->htmlOptions['id'] = $id;
		}
		if (isset($this->htmlOptions['name'])) {
			$name = $this->htmlOptions['name'];
		}
		$imgId = null;
		if (isset($this->imgHtmlOptions['id'])) {
			$imgId = $this->imgHtmlOptions['id'];
		}
		else {
			$imgId = $this->imgHtmlOptions['id'] = 'inpImg'.$id;
		}

		$deleteAppend = '_hidden';
		if ($this->hasModel()) {
			echo CHtml::activeFileField($this->model, $this->attribute, $this->htmlOptions);
		}
		else {
			echo CHtml::textField($name, $this->value, $this->htmlOptions);
		}
		$imgAlt = Yii::t('Site', 'Profile image');
		if (isset($this->imgHtmlOptions['alt'])) {
			$imgAlt = $this->imgHtmlOptions['alt'];
			unset($this->imgHtmlOptions['alt']);
		}
		echo CHtml::image($this->currentImageUrl, $imgAlt, $this->imgHtmlOptions);
		$settings = CJavaScript::encode(array(
			'deleteText' => $this->deleteControlTitle,
			'css' => $this->css,		
			'addPhotoText' => $this->addPhotoText,
			'deleteVar' => $this->deleteText,
			'deleteId' => CHtml::ID_PREFIX.$id,
			'placeholderImage' => $this->placeholderUrl,
			'imageTagSelector' => '#'.$imgId,
		));

		$js = "jQuery('#{$id}').imageUploadInput($settings);";
		$cs = Yii::app()->getClientScript();
		$cs->registerScript(__CLASS__.'#'.$id, $js, CClientScript::POS_READY);
	}


Так же напишим вспомогательный плагин на JQuery для нашего виджета на
cобытие возникающего после выбора изображения.
		self.bind('change.imageUploadInput', function(evt) {
				var files = evt.target.files; // получаем объекты файлов
				for (var i = 0, f; f = files[i]; i++) {
					if (!f.type.match('image.*')) { // фильтр только по изображениям
						continue;
					}
					var reader = new FileReader();  // сохраняем содержание в память
					reader.onload = (function(theFile) {
						return function(e) {
							displayImage.attr('src', e.target.result); // отображаем изображение пользователю в base64
						}
					})(f);
					reader.readAsDataURL(f); // получаем данные о файле в виде схемы data:URL
				}
			});


Во View формы определяем виджет не забыв прописать параметр формы'enctype' => 'multipart/form-data'
$form = $this->beginWidget('CActiveForm', array(
	'id' => 'page-form',
	'enableAjaxValidation' => false,<habracut /><habracut />
	'htmlOptions' => array(
		'accept-charset' => $this->charSet,
		'class' => 'model-form',
		'enctype' => 'multipart/form-data',
	),
)); 
		 $attribute = 'photo';
		$this->widget('ext.ImageUploadInputWidget.ImageUploadInputWidget', array(
			'model' => $model, 
			'attribute' => $attribute,
			'deleteText' => $model->deleteText, // текст для ссылки  удлить
			'currentImageUrl' => ($model->photoUrl === null ? null : Yii::app()->request->baseUrl.'/'.$model->photoUrl), // путь до изображения которое уже было добавлено, например из поля photoUrl
			'placeholderUrl' => Yii::app()->request->baseUrl.'/images/layout/pic_miniature_218x218.png', // путь до фото заглушки
			'htmlOptions' => array(
				'title' => Yii::t('Site', 'Click to change image...'), // Placeholder при наведении на изображнеи курсора
			),
		));
		 


 $this->endWidget();

Подключаем поведение в контроллере при создании или обновлении данных.
public function actionCreate() {
		$model->attachBehavior('photoUpload', array(
			'class' => 'ext.ImageUrlInputMD5Behavior',
			'savePath' => 'images/brands',
			'urlAttribute' => 'photoUrl', // название поля в модели для храненения пути к картинке
		));
 	}

Так же опишим необходимые Ruls для подключаемой модели.
	public function rules()
	{
		return array(
			array('title, siteUrl, countryId, photo', 'default', 'value' => null),
			array('photo',
				'file',
				'maxSize' => 200 * 10,
				'types' => array(
					'jpg',
					'jpeg',
					'png',
					'gif',
				),
				'allowEmpty' => true,
				'safe' => true,
			),
				);
	}

В итоге у нас получился достаточно удобный и легкий виджет для загрузки изображений, который можно без особой сложности применять к любой модели, так же если немного модифицировать код можно добиться поддержки miltiple загрузки картинок, и и обрабатывать их на сервере вместе с с остальными данными формы.

Ссылка на widget и behavior.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.