В одном из своих проектов, реализуемых на Yii, потребовалось сделать загрузку изображений, причем не хотелось использовать Flash и загрузку файлов через скрытый iframe. Задумавшись что мне не нужна поддержка ie меньше 10 версии я решил почему бы не использовать все прелести HTML5.
HTML5 предоставляет нам прекрасную возможность использовать загрузку изображений стандартными средствами используя File Api и работать с файлами через функцию
Загрузив и считав изображение на клиенте, через свойство result функции
Небольшой фрагмент кода метода run виджета.
Так же напишим вспомогательный плагин на JQuery для нашего виджета на
cобытие возникающего после выбора изображения.
Во View формы определяем виджет не забыв прописать параметр формы
Подключаем поведение в контроллере при создании или обновлении данных.
Так же опишим необходимые Ruls для подключаемой модели.
В итоге у нас получился достаточно удобный и легкий виджет для загрузки изображений, который можно без особой сложности применять к любой модели, так же если немного модифицировать код можно добиться поддержки miltiple загрузки картинок, и и обрабатывать их на сервере вместе с с остальными данными формы.
Ссылка на widget и behavior.
Немного теории
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.