Beginner WEB-developer Blog of WEB-design. Most development in Kohana. Writing the components and modules for Joomla!

17Янв/1114

HTML 5 мультизагрузка файлов в elFinder

Мультизагрузка в elFinder

Мультизагрузка в elFinder

Многие знают замечательную разработку "Студии 42" файловый менеджер elFinder.

elFinder - это файловый менеджер для веб-приложений, написанный на JavaScript с использованием jQuery UI. Как видно из названия, на его создание нас вдохновила простота и удобство программы Finder.app в операционной системе Mac OS X. (Сообщают нам разработчики этого замечательного продукта).

Остальные подробности, а также форум обсуждения elFinder и elRte на сайте http://elrte.org/

Передо мной встал вопрос использования файлового менеджера в своей CMS. соответственно начал искать варианты. Наткнулся на elRte и elFinder - все понравилось. но отсутствовала мультизагрузка файлов.

Стандартная загрузка файлов

Стандартная загрузка файлов в elFinder

Да там есть кнопочка добавления полей для загрузки, но это не очень удобно если нужно залить сразу 15-20 файлов.

На форуме пожелание добавить современную мультизагрузку в менеджер высказывались. Но разработчики пока не реализовали данный функционал. Поэтому оставлась либо ждать выхода следующей версии. либо ваять самому. Что я и сделал.

Итак я встроил мультизагрузку скриптом из collective.quickupload для Plone CMS http://plone.org/products/collective.quickupload
в нем используется модифицированная версия скрипта от Andrew Valums http://valums.com/ajax-upload/

Я думаю хватит слов я постараюсь объяснить, что поменял в elFinder`е и Ajax Upload от Andrew Valums.

Для начала скопировал файлы Valums в директорию elFinder'а:

[Plone] - директория из архива скачанного с http://plone.org/products/collective.quickupload/releases/1.0.3/collective.quickupload-1.0.3.tar.gz
[Valums] - директория из архива скачанного с http://valums.com/ajax-upload/
[elFinder] - анлогично

Копируем:

  • [Valums]/server/php.php в [elFinder]/connectors/php/valums.php (перименовал, чтобы было понятно, что за файл валяется в папке)
  • [Valums]/client/loading.gif в [elFinder]/js/loading.gif
  • [Plone]/collective/quickupload/browser/static/fileuploader.js в [elFinder]/js/fileuploader.js
  • [Plone]/collective/quickupload/browser/static/fileuploader.css в [elFinder]/js/fileuploader.css

Дальше:
Находим строчку 1938 в файле [elFinder]/js/elfinder.full.js и меняем после нее слудующий код:

upload : function(fm) {
		var self  = this;
		this.name = 'Upload files';
		this.fm   = fm;
		
		this.exec = function() {

			var id = 'el-finder-io-'+(new Date().getTime()),
				e = $('<div class="ui-state-error ui-corner-all"><span class="ui-icon ui-icon-alert"/><div/></div>'),
				m = this.fm.params.uplMaxSize ? '<p>'+this.fm.i18n('Maximum allowed files size')+': '+this.fm.params.uplMaxSize+'</p>' : '',
				b = $('<p class="el-finder-add-field"><span class="ui-state-default ui-corner-all"><em class="ui-icon ui-icon-circle-plus"/></span>'+this.fm.i18n('Add field')+'</p>')
					.click(function() { $(this).before('<p><input type="file" name="upload[]"/></p>'); }),
				f = '<form method="post" enctype="multipart/form-data" action="'+self.fm.options.url+'" target="'+id+'"><input type="hidden" name="cmd" value="upload" /><input type="hidden" name="current" value="'+self.fm.cwd.hash+'" />',
				d = $('<div/>'),
				i = 3;

				while (i--) { f += '<p><input type="file" name="upload[]"/></p>'; }
				f = $(f+'</form>');
				
				d.append(f.append(e.hide()).prepend(m).append(b)).dialog({
						dialogClass : 'el-finder-dialog',
						title       : self.fm.i18n('Upload files'),
						modal       : true,
						resizable   : false,
						close       : function() { self.fm.lockShortcuts(); },
						buttons     : {
							Cancel : function() { $(this).dialog('close'); },
							Ok     : function() {
								if (!$(':file[value]', f).length) {
									return error(self.fm.i18n('Select at least one file to upload'));
								}
								setTimeout(function() {
									self.fm.lock();
									if ($.browser.safari) {
										$.ajax({
											url     : self.fm.options.url,
											data    : {cmd : 'ping'},
											error   : submit,
											success : submit
										});
									} else {
										submit();
									}
								});
								$(this).dialog('close');
							}
						}
					});

			self.fm.lockShortcuts(true);

на

upload : function(fm) {
		var self  = this;
		this.name = 'Upload files';
		this.fm   = fm;
		
		// Добавляем событие. по которому будет осуществлятся загрузка добавленных файлов
		$(".qq-upload-bb").live("click",function(){ 
			uploader._uploadAll();
		});
		
		// Событие для очистки очереди загрузки
		$(".qq-clear").live("click",function(){
			uploader._cancelAll();
			$(".qq-upload-list").html("");
		});
		
		this.exec = function() {	
			// Создаем объект  FileUploader`а 
			function createUploader(){            
				var uploader = new qq.FileUploader({
					// Указываем куда вставлять загрузчик
					element: document.getElementById('demo'),
					listElement: document.getElementById('separate-list'),
					action: self.fm.options.url,
					//Ограничение на размер файла
					sizeLimit: 5000000,
					minSizeLimit: 100,
					//автоматическая загрузка сразу после добавления файлов
					autoUpload:false,
					//Количество одновременный загрузок (если ставить чило больше прирост заметен, но я решил ограничится двуми потоками)
					simUploadLimit: 2,
					//Разрешенные типы файлов
					allowedExtensions: ['jpg', 'jpeg', 'png', 'gif'],
					//Передаем параметры коннектору elFindera
					params: {'cmd':'upload','current':self.fm.cwd.hash}, 
					// current -  хэш текущего пути
					// cmd - действие для коннектора elFindera
					onComplete: function(id, fileName, responseJSON){
						if (uploader._filesInProgress==0){
							//uploader._countUnLoad() - метод добавлен мной, вычисляет количество незагруженных файлов на данный момент
							if (uploader._countUnLoad()==1){
								//Обновляем окошко elFinder
								fm.ui.exec('reload');
								dialog_upload.remove();
							}
						}
					}
				});  
				return uploader;
			} 		
						
			dialog_upload = $('<div><div id="demo"></div><ul id="separate-list"></ul></div>').dialog({
				dialogClass : 'el-finder-dialog',
				title       : self.fm.i18n('Upload files'),
				modal       : true,
				width		: 500,
				height		: 410,
				resizable   : false,
				close       : function() { $(this).remove();self.fm.lockShortcuts(); },
				buttons     : {
					Cancel : function(data) { $(this).dialog('destroy');
						uploader._cancelAll();
						fm.ui.exec('reload');
						$(this).remove();
					}}
			});			
			// Запускаем функцию инициализирующую мультизагрузку		
			uploader = createUploader();	
			self.fm.lockShortcuts(true);

В файле [elFinder]/js/fileuploader.js добавляем или меняем функции:

строка 42:

// UI customizations
template: '<div class="qq-uploader">' + 
	'<div class="qq-upload-drop-area"><span>Drop files here to upload</span></div>' +
		'<div class="qq-upload-button">Browse for a file</div>' +
		'<ul class="qq-upload-list"></ul>' + 
	'</div>',

// template for one item in file list
fileTemplate: '<li>' +
	'<a class="qq-upload-cancel" href="#">&nbsp;</a>' +
		'<div class="qq-upload-infos"><span class="qq-upload-file"></span>' +
			'<span class="qq-upload-spinner"></span>' +
			'<span class="qq-upload-failed-text">Failed</span></div>' +
		'<div class="qq-upload-size"></div>' +
	'</li>',

на

// UI customizations
template: '<div class="qq-uploader">' + 
				'<div class="qq-header"><div>Filename</div></div>' +
				'<div class="qq-upload-drop-area"></div>' +
				'<ul class="qq-upload-list"></ul>' + 
				'<div class="qq-bar"><div class="qq-upload-button">Add to list</div>'+
				'<div class="qq-upload-bb">Upload</div>' +
				'<div class="qq-clear">Clear</div></div>' +
		  '</div>',

// template for one item in file list
fileTemplate: '<li>' +
			'<a class="qq-upload-cancel" href="#">&nbsp;</a>' +
				'<div class="qq-upload-infos"><span class="qq-upload-file"></span>' +
				'<span class="qq-upload-spinner"></span>' +
				'<span class="qq-upload-failed-text">Failed</span></div>' +
				'<div class="qq-upload-size"></div>' +
			'</li>',

изменяем:

_addSelection: function(files) {
	var i = files.length;
		while (i--){         
			if (this._validateFile(files[i])){
				this._addFile(files[i]);
			}
		}  
},		

добавляем:

_countUnLoad: function() {
	var allfiles = this._handler._files;
	var count = 0;				
	for ( var id = 0; id < allfiles.length; id++ ) {
		if (allfiles[id]) {
			count++;
		}
	}
	return count;
},    

добавляем в [elFinder]/images картинки:

  • val_upload.png
  • val_delete.png
  • val_clear.png
  • val_add.png

Далее переходим к серверной части:

в [elFinder]/connectors/php/elFinder.class.php добавляем строчку после 19

	// Максимальный объем папки для загрузки
	'maxRootRize'  => 10000000, 

заменяем в нем же:

/**
 * Upload files
 *
 * @return void
 **/
protected function _upload()
{
	if (empty($_POST['current']) 
	|| false == ($dir = $this->_findDir(trim($_POST['current'])))) {
		return $this->_result['error'] = 'Invalid parameters';
	} 
	if (!$this->_isAllowed($dir, 'write')) {
		return $this->_result['error'] = 'Access denied';
	}
	if (empty($_FILES['upload']))
	{
		return $this->_result['error'] = 'No file to upload';
	}
		
	$this->_logContext['upload'] = array();
	$this->_result['select'] = array();
	$total = 0;
	for ($i=0, $s = count($_FILES['upload']['name']); $i < $s; $i++) { 
		if (!empty($_FILES['upload']['name'][$i])) {
			$total++;
			$this->_logContext['upload'][] = $_FILES['upload']['name'][$i];
			if ($_FILES['upload']['error'][$i] > 0) {
				$error = 'Unable to upload file';
				switch ($_FILES['upload']['error'][$i]) {
					case UPLOAD_ERR_INI_SIZE:
					case UPLOAD_ERR_FORM_SIZE:
						$error = 'File exceeds the maximum allowed filesize';
						break;
					case UPLOAD_ERR_EXTENSION:
						$error = 'Not allowed file type';
						break;
				}
				$this->_errorData($_FILES['upload']['name'][$i], $error);
			} elseif (false == ($name = $this->_checkName($_FILES['upload']['name'][$i]))) {
				$this->_errorData($_FILES['upload']['name'][$i], 'Invalid name');
			} elseif (!$this->_isUploadAllow($_FILES['upload']['name'][$i], $_FILES['upload']['tmp_name'][$i])) {
				$this->_errorData($_FILES['upload']['name'][$i], 'Not allowed file type');					
			} else {
				$file = $dir.DIRECTORY_SEPARATOR.$_FILES['upload']['name'][$i];
				if (!@move_uploaded_file($_FILES['upload']['tmp_name'][$i], $file)) {
					$this->_errorData($_FILES['upload']['name'][$i], 'Unable to save uploaded file');	
				} else {
					@chmod($file, $this->_options['fileMode']);
					$this->_result['select'][] = $this->_hash($file);
				}
			}
		}
	}
	$errCnt = !empty($this->_result['errorData']) ? count($this->_result['errorData']) : 0;
	if ($errCnt == $total) {
		$this->_result['error'] = 'Unable to upload files';
	} else {
		if ($errCnt>0) {
			$this->_result['error'] = 'Some files was not uploaded';	
		}
		$this->_content($dir);
	}	
}	

на

/**
* Upload files
 *
 * @return void
 **/
protected function _upload()
{
	//Подключаем библиотеку Valums
	include_once 'valums.php';
	//$_GET['current'] - хэш пути переданный fileuploader.js
	if (empty($_GET['current']) 
		|| false == ($dir = $this->_findDir(trim($_GET['current'])))) {
		return $this->_result['error'] = 'Invalid parameters';
	} 
	if (!$this->_isAllowed($dir, 'write')) {
		return $this->_result['error'] = 'Access denied';
	}	
	$allowedExtensions = array();
	// max file size in bytes
	$sizeLimit = 5 * 1024 * 1024; 
		
	$defaultRootSize = $this->_dirSize($this->_options['root']);
	// Проверяем размер папки в которую идет загрузка
	if ($defaultRootSize<$this->_options['maxRootRize']){
		$uploader = new qqFileUploader($allowedExtensions, $sizeLimit);
		$result = $uploader->handleUpload($dir.DIRECTORY_SEPARATOR);
		return $this->_result['success'] = 'upload';
	}
	else{
		return $this->_result['error'] = 'The size of the root within a directory exceeds the allowable size';
	}				
}

Ну и конечно оформление в файле [elFinder]/js/fileuploader.css меняем соответствующим образом.

Результат можно посмотреть на http://sloin.ru/elfinder-demo
Сделал ограничение на размер папки загрузки в 100МВ и типы файлов картинки.

Архив с результатами http://sloin.ru/files/elfinder-demo.zip

PS. Существует несколько проблем:

  1. В хроме при выборе определенного количества файлов не происходит добавление в очередь.
  2. Не сделал, так чтобы при загрузке файлов нельзя было очистить очередь.
Комментарии (14) Пинги (0)
  1. Вот бы допилить еще чтоб:
    1. картинка уменьшалась до заданных размеров (например 1000*700)
    2. в папке tmb создавалась как и счас еще более уменьшеная копия (100*70) НО только с нормальным именем таким как же как в 1.
    3. ватер надпись поверх наложить на 1.

    • На самом деле это не очень сложно.
      Я у себя в CMS реализую частично. А включать имеенно в мультизагрузку смысла нет.

  2. ага, я уже сделал себе

  3. $sizeLimit = 5 * 1024 * 1024;

    еще из за вот этого проблемки на разных хостингах, так как приходится по коду искать и менять, у себя поставил через настройку в одном месте

    • Отлично, надо будет у себя тоже сделать. Когда встраивал, главное было работоспособность, не сильно думал на счет удобств.

  4. А подскажите, как поменять лимит на размер загружаемого файла? :)

    Заранее спасибо ))

    • В серверной части –
      1. файл elfinder-demo\connectors\php\elFinder.class.php – в нем строчка:
      // max file size in bytes
      $sizeLimit = 5 * 1024 * 1024; – Здесь и меняем на нужный.
      2. файл elfinder-demo\connectors\php\php.ini:
      upload_max_filesize = 5M
      В клиентской части –
      3. elfinder-demo\js\elfinder.full.js – строчка 1961
      sizeLimit: 5000000, // max size

      • спасибо за ответ..

        но все-равно не хочет заливать больше 10 мб файл.

        сервер фри-бсдшный. в глобальном пхп.ини upload_max_filesize = 32M

        в чем может быть проблема?

        • Тоже попробовал менять на большие значения. Не стабильно работает загрузка. Я более 5 метров не смог закачать.
          Надо разбираться более глубоко в HTML 5 загрузке.
          Valums исходники смотреть. Возможно на сервере какие-то еще директивы выставлять надо.

  5. Вот бы доделать, чтобы когда жмешь загрузить файлы — сразу вылатало окошко для выбора файлов, и не нужно было нажимать еще кнопку ДОБАВИТЬ

  6. А с загрузкой больших файлов не разбирались, случайно? :)

  7. Как «под виндой с»обрать проект из фалойв скаченных отсюда
    https://github.com/Studio-42/


Leave a comment

Нет обратных ссылок на эту запись.