HTML 5 мультизагрузка файлов в elFinder
Многие знают замечательную разработку "Студии 42" файловый менеджер elFinder.
elFinder - это файловый менеджер для веб-приложений, написанный на JavaScript с использованием jQuery UI. Как видно из названия, на его создание нас вдохновила простота и удобство программы Finder.app в операционной системе Mac OS X. (Сообщают нам разработчики этого замечательного продукта).
Остальные подробности, а также форум обсуждения elFinder и elRte на сайте http://elrte.org/
Передо мной встал вопрос использования файлового менеджера в своей CMS. соответственно начал искать варианты. Наткнулся на elRte и 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="#"> </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="#"> </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. Существует несколько проблем:
- В хроме при выборе определенного количества файлов не происходит добавление в очередь.
- Не сделал, так чтобы при загрузке файлов нельзя было очистить очередь.
Понравилась статья?
Нет обратных ссылок на эту запись.
Март 30th, 2011 - 21:44
Вот бы допилить еще чтоб:
1. картинка уменьшалась до заданных размеров (например 1000*700)
2. в папке tmb создавалась как и счас еще более уменьшеная копия (100*70) НО только с нормальным именем таким как же как в 1.
3. ватер надпись поверх наложить на 1.
Март 31st, 2011 - 17:52
На самом деле это не очень сложно.
Я у себя в CMS реализую частично. А включать имеенно в мультизагрузку смысла нет.
Апрель 1st, 2011 - 13:34
ага, я уже сделал себе
Апрель 1st, 2011 - 13:36
$sizeLimit = 5 * 1024 * 1024;
еще из за вот этого проблемки на разных хостингах, так как приходится по коду искать и менять, у себя поставил через настройку в одном месте
Апрель 1st, 2011 - 13:51
Отлично, надо будет у себя тоже сделать. Когда встраивал, главное было работоспособность, не сильно думал на счет удобств.
Апрель 2nd, 2011 - 13:09
А подскажите, как поменять лимит на размер загружаемого файла?
Заранее спасибо ))
Апрель 2nd, 2011 - 13:48
В серверной части –
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
Апрель 2nd, 2011 - 16:38
спасибо за ответ..
но все-равно не хочет заливать больше 10 мб файл.
сервер фри-бсдшный. в глобальном пхп.ини upload_max_filesize = 32M
в чем может быть проблема?
Апрель 2nd, 2011 - 17:33
Тоже попробовал менять на большие значения. Не стабильно работает загрузка. Я более 5 метров не смог закачать.
Надо разбираться более глубоко в HTML 5 загрузке.
Valums исходники смотреть. Возможно на сервере какие-то еще директивы выставлять надо.
Апрель 11th, 2011 - 22:06
Вот бы доделать, чтобы когда жмешь загрузить файлы — сразу вылатало окошко для выбора файлов, и не нужно было нажимать еще кнопку ДОБАВИТЬ
Апрель 11th, 2011 - 22:42
Это несложно, сделаю опционально когда появится время.
Апрель 21st, 2011 - 18:45
А с загрузкой больших файлов не разбирались, случайно?
Апрель 24th, 2011 - 10:44
Насколько помню загружал файлы метров по 40-50 — все нормально было. Больше не тестил.
Май 16th, 2011 - 20:06
Как «под виндой с»обрать проект из фалойв скаченных отсюда
https://github.com/Studio-42/