HTML5 Drag-n-Drop загрузка файлов

Подготовили: Евгений Рыжков и Егор Скорняков Дата публикации: 12.06.2011

Задача

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

Решение

Смотрим демо пример. Пример можно забрать архивом. Проверено:

  • IE 6-9
  • Firefox 3.6-4
  • Opera 11.1
  • Chrome
  • Safari 5

Обращу внимание, что в данном случае проверено != работает:

  • IE включая 9-ю версию не поддерживает File API (старая реализация);
  • Firefox 3.6+ поддерживает все, что нужно. Для более старых версий — старая реализация;
  • Opera 11.1 поддерживает File API, но не поддерживает DnD;
  • Chrome, начиная с 10-й версии, поддерживает все, что нужно;
  • Safari поддерживает все что нужно с 6-й версии.

Какой толк от этого в таком случае? Пользователи нормальных браузеров получают более удобные сайты.

Что качать:

Быстрый старт

Подключаем скрипты, а в форму добавляет input type=file с id. Если браузер не поддерживает необходимый набор API, пользователь сможет загрузить фото «по старинке». Чтобы у него была возможность добавить несколько фото, динамически добавим кнопку «+», которая будет добавлять поля input type=file. Для этих целей в id поля присутствует 0, чтобы проще было организовать правильные имена добавленных полей.

<script src="path-to/jquery.min.js"></script>
<script src="path-tp/jquery.client.js" charset="utf-8"></script>
...
<input type="file" id="fileUpload0" multiple="true" size="60">

Для браузеров, которые поддерживают Dnd и File API можно поля type=file вообще скрыть или удалить.

Как это работает

Если не вдаваться особо в детали, тогда принцип работы можно отобразить в виде подобной схемы:

схема работы HTML5 DnD загрузки файлов
  1. (DnD API) все начинается когда пользователь отпускает зажатую кнопку мыши — срабатывает событие drop;
  2. (DnD API) получаем объект DataTransfer от события drop;
  3. (File API) вызывая DataTransfer.files получаем FileList — список файлов, которые пользователь перетащил в область загрузки;
  4. (File API) перебирая все файлы читаем их содержимое с помощью объекта FileReader;
  5. (File API) вызывая FileReader.readAsDataURL(file) каждый раз, когда очередной файл удачно прочитан, формируем data URL объект. Когда он полностью будет сформирован произойдет событие onloadend объекта FileReader;
  6. когда мы получили объект data:URL, мы можем подставить эти данные в src и отобразить изображение, а так же отправить данные в двоичном виде на сервер;
  7. (XMLHttpRequest 2) асинхронно отправляем данные, а с помощью новых фишек второй версии протокола XMLHttpRequest получаем данные о состоянии загрузки (событие progress), что позволяет информировать пользователя.

Fine Uploader Plugin

update: 18.09.12 by Андрей Косяк.

Довольно удобный js-плагин по заливке файлов на сервер. Написан на чистом JS. Состоит из серверной части ( на нескольких языках ) и клиентской ( JS и CSS ).

НЕ использует флеш и какие-либо фреймворки.

Fineuploader использует XMLHttpRequest объект для передачи файлов с прогрессбаром для современных браузеров, а для всякого треша используется способ со старым добрым iframe-ом.

Знакомимся:

Проверено:

  • IE 7-9
  • Firefox 3.6+
  • Opera 10.6+
  • Chrome
  • Safari 4+

Быстрый старт

Работоспособность плагина нужно проверять исключительно с сервера ( локальный подойдет ). В примере буду использовать серверную часть на PHP.

Перед первым стартом нужно подготовить почву:

  • На локальном сервере первое, что попросит скрипт, это увеличить значения параметров post_max_size и upload_max_filesize. Открываем файл php.ini находим эти параметры и ставим значения >= 10M. На реальном сервере скорее всего этот пункт не потребуется.

  • Нужно создать папку uploads ( далее будем считать, что она создана в корне сайта ) и :

    • если тестируем на веб сервере, то ставим ей права 777
    • если тестируем на локальном, то в свойствах папки убираем галочку "только для чтения" ( может работать и без этого )
  • из папки /server плагина забираем файл php.php. В нем, в первом блоке комментариев написано, какие строки нужно извлечь из комментариев, чтобы инициализировать класс. Добавляем сразу после блока с комментарием:

    // допустимые расширения файлов
    $allowedExtensions = array();
    
    // максимальный размер файла
    $sizeLimit = 10 * 1024 * 1024;
    
    // инициализация класса
    $uploader = new qqFileUploader($allowedExtensions, $sizeLimit);
    
    // тут нужно указать путь к папке /uploads
    $result = $uploader->handleUpload("../uploads/");
    
    // возврат ответа после сохранения файла
    echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
    

Подключаем скрипт плагина и стили:


<link rel="stylesheet" href="css/fileuploader.css">
<script src="js/fileuploader.js"></script>

HTML-код:


<div id="file-uploader">
    <noscript>
        <p>Please enable JavaScript to use file uploader.</p>
    </noscript>
</div>

JS-инициализация:

window.onload = function(){

	var uploader = new qq.FileUploader({
			element: document.getElementById('file-uploader1'),
			action: 'php/upload.php'    // путь к серверной части плагина
	});
};
  

Результат

Как видно из примера выше, файлы отправляются на сервер сразу же после выбора. Отправим их по событию:

window.onload = function(){
	
	var uploader = new qq.FileUploader({
			autoUpload: false,
			element: document.getElementById('file-uploader'),
			action: 'php/upload.php'
	});
	
	$( '#startUpload' ).on( 'click', function(){
		uploader.uploadStoredFiles();
	} );
	
	
};
Результат

В целом у плагина много опций и настроек, имеются колбеки. Все это описано тут. Fineuploader находится в постоянной разработке, поэтому рекомендую при привязке посещать репозиторий и брать свежую версию.

Стилизация

Плагин создает свою структуру на базе класса .qq-uploader, все элементы описаны в css файле fileuploader.css

На заметку

Если ослы упорно отказываются работать, открываем скрипт, ищем там строку:

var form = qq.toElement('<form method="' + protocol + '" enctype="multipart/form-data"></form>');

и меняем на:

var form = qq.toElement('<form method="' + protocol + '" enctype="application/octet-stream"></form>');

Материалы

Показать комментарии