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/