Yii: Множественная загрузка с помощью расширения xupload
Столкнулся с проблемой, когда CMultiFileUpload не могло загружать файлы с iphone. Все изображения при такой загрузке именуются одинаково image.jpg и виджет выдаёт ошибку, что файл с таким именем уже добавлен, несмотря на то, что это другое изображение, просто имя совпадает. Пришлось найти другой виджет для множественной загрузки. Про загрузку с CMultiFileUpload писали в статье Yii: Форма Contact Form с загрузкой файлов CMultiFileUpload и отправка на email с YiiMailer.
На замену этомурасширению нашлось популярное xupload, которое основано на jQuery file upload.
Тут описан подробный процесс подключения расширения в вашу форму - http://www.yiiframework.com/wiki/348/xupload-workflow/
Отмечу некоторые особенности.
1. Либо вы используете экшн, предлагаемый расширением и тогда прописываете его в actions() вашего контроллера
public function actions()
{
return array(
...
'upload' => array('class' => 'xupload.actions.XUploadAction', 'fileAttribute'=>'file', 'path' => Yii::app() -> getBasePath() . "/../images/uploads", "publicPath" => Yii::app()->getBaseUrl()."/images/uploads" ),
);
}
либо это не пишете и прописываете в контроллере этот экшн (за основу можно взять и скопировать экшн из расширения)
public function actionUpload( ) {
Yii::import( "xupload.models.XUploadForm" );
//Here we define the paths where the files will be stored temporarily
$path = realpath( Yii::app( )->getBasePath( )."/../images/uploads/tmp/" )."/";
$publicPath = Yii::app( )->getBaseUrl( )."/images/uploads/tmp/";
//This is for IE which doens't handle 'Content-type: application/json' correctly
header( 'Vary: Accept' );
if( isset( $_SERVER['HTTP_ACCEPT'] )
&& (strpos( $_SERVER['HTTP_ACCEPT'], 'application/json' ) !== false) ) {
header( 'Content-type: application/json' );
} else {
header( 'Content-type: text/plain' );
}
//Here we check if we are deleting and uploaded file
if( isset( $_GET["_method"] ) ) {
if( $_GET["_method"] == "delete" ) {
if( $_GET["file"][0] !== '.' ) {
$file = $path.$_GET["file"];
if( is_file( $file ) ) {
unlink( $file );
}
}
echo json_encode( true );
}
} else {
$model = new XUploadForm;
$model->file = CUploadedFile::getInstance( $model, 'file' );
//We check that the file was successfully uploaded
if( $model->file !== null ) {
//Grab some data
$model->mime_type = $model->file->getType( );
$model->size = $model->file->getSize( );
$model->name = $model->file->getName( );
//(optional) Generate a random name for our file
$filename = md5( Yii::app( )->user->id.microtime( ).$model->name);
$filename .= ".".$model->file->getExtensionName( );
if( $model->validate( ) ) {
//Move our file to our temporary dir
$model->file->saveAs( $path.$filename );
chmod( $path.$filename, 0777 );
//here you can also generate the image versions you need
//Используем функции расширения CImageHandler;
$ih = new CImageHandler(); //Инициализация
Yii::app()->ih
->load($path.$filename) //Загрузка оригинала картинки
->thumb('150', false) //Создание превьюшки высотой 125px
->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/uploads/tmp/thumbs/'.$filename) //Сохранение превьюшки
;
//Now we need to save this path to the user's session
if( Yii::app( )->user->hasState( 'images' ) ) {
$userImages = Yii::app( )->user->getState( 'images' );
} else {
$userImages = array();
}
$userImages[] = array(
"path" => $path.$filename,
//the same file or a thumb version that you generated
"thumb" => 'thumbs/'.$path.$filename,
"filename" => $filename,
'size' => $model->size,
'mime' => $model->mime_type,
'name' => $model->name,
);
Yii::app( )->user->setState( 'images', $userImages );
//Now we need to tell our widget that the upload was succesfull
//We do so, using the json structure defined in
// https://github.com/blueimp/jQuery-File-Upload/wiki/Setup
echo json_encode( array( array(
"name" => $model->name,
"type" => $model->mime_type,
"size" => $model->size,
"url" => $publicPath.$filename,
"thumbnail_url" => $publicPath."thumbs/$filename",
"delete_url" => $this->createUrl( "upload", array(
"_method" => "delete",
"file" => $filename
) ),
"delete_type" => "POST"
) ) );
} else {
//If the upload failed for some reason we log some data and let the widget know
echo json_encode( array(
array( "error" => $model->getErrors( 'file' ),
) ) );
Yii::log( "XUploadAction: ".CVarDumper::dumpAsString( $model->getErrors( ) ),
CLogger::LEVEL_ERROR, "xupload.actions.XUploadAction"
);
}
} else {
throw new CHttpException( 500, "Could not upload file" );
}
}
}
public function accessRules()
{
return array(
array('allow', // allow all users to perform 'index' and 'view' actions
'actions'=>array('captcha','create','list','upload'),
'users'=>array('*'),
),
...
3. Виджет на форме создаёт по умолчанию свой тег <form>, что не нужно, если у вас своя форма с полями и этот виджет лишь часть её. Решения возможны 2: либо делаете 'showForm'=>false, либо указываете 'formView' => 'application.views.maya._upload', и создаёте свой вид _upload.php (копируете из расширения, только обёртку в форму не берёте)
Вот мой вариант с 'showForm'=>false.
<?php $this->widget('xupload.XUpload', array(
'url' => Yii::app()->createUrl("maya/upload"),
'model' => $photos,
'htmlOptions' => array('id'=>'dancers-form'),
'attribute' => 'file',
'multiple' => true,
'autoUpload' => true,
'showForm'=>false,
//'formView' => 'application.views.maya._upload',
'options'=>array(
'maxNumberOfFiles'=>6,
'maxFileSize'=>3000000,
'onComplete' => 'js:function (event, files, index, xhr, handler, callBack) {
$("#Maya_attachments").val(\'\'+handler.response.name + \'\' );
}'
),
));?>
4. Хорошо ставить 'autoUpload=>true' как выше в моём виджете видно. Всё таки пользователь в итоге должен заполнив поля, нажать только кнопку "Отправить", и не нажимать дополнительно "Загрузить приложенные файлы".
5. У меня форма отправлялась на email с вложенными файлами. Необходимо в конце действия очищать сессию - переменную 'image', иначе будут отправляться файлы с предыдущих заполненений формы. Вот мой экшн
public function actionCreate()
{
$this->layout='maya_create';
$model=new Maya('insert');
Yii::import( "xupload.models.XUploadForm" );
$photos = new XUploadForm;
$this->performAjaxValidation($model);
if(isset($_POST['Maya']))
{
$model->attributes=$_POST['Maya'];
if(Yii::app()->params['dancersNeedApproval'])
$model->status=Maya::STATUS_PENDING;
else
$model->status=Maya::STATUS_APPROVED;
//$model->icon=CUploadedFile::getInstance($model,'icon');
$transaction = Yii::app( )->db->beginTransaction( );
try {
//Save the model to the database
if($model->save()){
$transaction->commit();
//используем представление 'contact' из директории views/mail
$mail = new YiiMailer('contact', array(
'message' => 'Дисциплина: <b>'.Lookup::item('Discipline', $model->discipline_id).'</b><br />
ФИО: <b>'.$model->surname.'</b><br />
Ники: <b>'.$model->name.'</b><br />
Команда: <b>'.$model->crew.'</b><br />
Город: <b>'.$model->city_id.'</b><br />
Тел: <b>'.$model->phone.'</b><br />
email: <b>'.$model->email.'</b>',
'name' => $model->name,
'description' => 'Регистрация на участие'));
//Прикрепляем к сообщению загруженные файлы с помощью setAttachment()
$attachments = Yii::app( )->user->getState( 'images' ); //explode(',', $model->attachments);
//print_r($attachments[0]['filename']);
if (count($attachments)) {
foreach ($attachments as $file) {
$mail->setAttachment('images/uploads/tmp/'.$file['filename']);
}
}
//устанавливаем свойства
$mail->setFrom($model->email, $model->name);
$mail->setSubject("Заявка #".$model->id." с сайта wild-style.ru/register: ".$model->name);
$mail->setTo(Yii::app()->params['adminEmail']);
//отправляем сообщение
if ($mail->send()) {
Yii::app()->user->setFlash('dancerSubmitted','Спасибо за ваше письмо! Мы ответим вам в ближайшее время.');
Yii::app()->getController()->createAction('captcha')->getVerifyCode(true);
} else {
Yii::app()->user->setFlash('error','Какая-то ошибка: '.$mail->getError());
}
$this->refresh();
Yii::app( )->user->setState( 'images', null ); // очистить сессию, иначе в письмо прикрепляются все ранее загруженные картинки!
}
} catch(Exception $e) {
$transaction->rollback( );
Yii::app( )->handleException( $e );
}
}
$this->render('create',array(
'model'=>$model,
'photos' => $photos,
));
}
Это рабочий код, но грязноватый, тут есть что улучшать, например, давать файлам свои названия.

almix
Разработчик Loco, автор статей по веб-разработке на Yii, CodeIgniter, MODx и прочих инструментах. Создатель Team Sense.
Вы можете почитать все статьи от almix'а.
- 14 Разработка приложения на Yii. Урок 14: Выводим список категорий как новый виджет на сайте (29.04.2015)
- 13 Разработка приложения на Yii. Урок 13: Внедряем категории для статей. (29.04.2015)
- 12 Разработка приложения на Yii. Урок 12: Профилирование приложения, включаем кеширование. (20.01.2015)
- 11 Yiico. Видеокурс по разработке сайта на Yii. Урок 11: Отладка приложения, включаем журналирование. (20.01.2015)
- 10 Yiico. Видеокурс по разработке сайта на Yii. Урок 10: Если ваше приложение находится не в корневой папке, а во вложенной. (19.10.2014)
- 9 Разработка сайта на Yii с нуля. Урок 9. Выборка статей определённого автора. (13.08.2014)
- 8 Разработка сайта на Yii с нуля. Урок 8. Вызов в моделях функции, общей для них. Как избегать дублирования кода? (25.07.2014)
- 7 Разработка сайта на Yii с нуля. Урок 7. Изменение количества выводимых записей на странице в CGridView. Включаем сессии Yii. (04.09.2013)
- 6 Разработка сайта на Yii с нуля. Урок 6. Автоматич. отправка оповещений об одобренных комментариях на email автора комментария (11.08.2013)
- 5 Разработка сайта на Yii с нуля. Урок 5. Переименовываем blog в yiico. Изменяем "Home" в breadcrumbs. Включаем Gzip-сжатие. (09.08.2013)
- 4 Курс по Yii с нуля. Урок 4. Дорабатываем простую работу с пользователями: хранение пароля при редактировании пользователя. (13.04.2013)
- 3 Курс по Yii с нуля. Урок 3. Создаём новых пользователей. Организуем простую систему авторизации. Аутентификация, пароли, соли. (20.07.2014)
- 2 Yii + Git (github) на Mac. (24.03.2013)
- 2 Курс по Yii с нуля. Урок 2. Переносим и настраиваем Yii и проект нашего сайта на рабочем сервере. Избавляемся от index.php в url (09.07.2014)
- 1 Курс по Yii с нуля. Урок 1. Устанавливаем Yii на локальном компьютере. Заводим проект будущего сайта. (10.03.2013)
- 0 Composer – пакетный менеджер PHP. Что и как? (22.07.2014)
- 0 MySQL: проверить содержится ли значение в поле столбца (в столбце хранится строка значений через запятую) (22.05.2014)
- 0 Yii: Статичные страницы (создание, редактирование, удаление) (28.04.2014)
- 0 Yii: Расширение ECKEditor = Связка ckeditor + kcfinder (визуальный редактор с бесплатным файловым менеджером) (28.04.2014)
- 0 Yii: Bootstrap tabs, делаем активной вкладку на которую выполняется переход по ссылке (20.03.2014)
- 0 Yii: Доступ к атрибуту модели из файла шаблона (Вызов в шаблоне какого-либо атрибута модели). (17.02.2014)
- 0 Yii: Множественный автокомплит с помощью CJuiAutoComplete (автокомплит нескольких значений в одно поле) (07.02.2014)
- 0 Yii: Фотогалерея через поведение (доработка расширения imagesgallerymanager) (08.03.2016)
- 0 Yii: Установка и настройка Yii-app заготовки приложения от Crisu83 (09.03.2014)
- 0 Twitter Bootstrap Carousel Crossfade (09.09.2013)


