Завантаження файлу перетягуванням у браузер

Колись, як подивився відео презентації Google Wave, дуже сподобалася можливість завантаження файлів на сервер просто перетягнувши їх з робочого стола у вікно браузера. Потім почав користуватися Google Wave і випробував цю можливість власноруч. Після цього захотілося розібратися, як це працює.


Отже. Основна вимога для завантаження файлів перетягуванням - у браузері повинно бути встановлено Google Gears. З підтримкою Google Gears браузерами - ситуація наступна.
Chrome, IE - існує, Firefox - є певні особливості. Opera - Gears підтримується тільки в Mobile версії.

Для роботи прикладу потрібна також бібліотека gears_init.js. Її можна завантажити і підключати локально, чи віддалено.

<script src="http://code.google.com/apis/gears/gears_init.js" type="text/javascript" charset="utf-8"></script>

Також необхідно підключити бібліотеку JQuery.

На сторінці потрібно створити область, куди будет перетягуватися файл.

<div id="dropzone" style="..."></div>

Чи встановлено Google Gears в браузері можна перевірити наступним чином.
if (!window.google || !google.gears) {
    location.href = "http://gears.google.com/?action=install&message=<your welcome message>" + "&return=<your website url>";
}
Далі необхідно ініціалізувати наступні об'єкти:
var desktop = google.gears.factory.create('beta.desktop');
var request = google.gears.factory.create('beta.httprequest');
desktop -потрібний для підтримки робочого стола.
request - для відправки запиту на сервер.

Реєстрація обробників. Для кожного з браузерів їх назви відрізняються. Окрім завантаження, тут також буде змінюватися колір області, коли файл потрапляє в її межі і покидає її. get(0) - необхідно, щоб вибирати один, перший елемент dropzone.

Про особливості в Firefox. Якщо версія < 3.5 - подія, при якій спрацьовує обробник, називається "dragdrop". У версії 3.5 вона називається "drop".
if ($.browser.mozilla) {
       $('#dropzone')
           .get(0)
           .addEventListener('dragdrop', upload, false);
       $('#dropzone')
           .get(0)
           .addEventListener('dragenter', function(event) { $('#dropzone').css("background-color", "#ffc"); }, false);
       $('#dropzone')
           .get(0)
           .addEventListener('dragexit', function(event) { $('#dropzone').css("background-color", "#fff"); }, false);
   } else if ($.browser.msie) {
       $('#dropzone')
       	    .get(0)
           	.attachEvent('ondrop', upload, false);
       	$('#dropzone')
           	.get(0)
           	.attachEvent('ondragover', function(event) { event.returnValue = false; }, false);
       	$('#dropzone')
           	.get(0)
           	.attachEvent('ondragenter', function(event) { $('#dropzone').css("background-color", "#ffc"); }, false);
       	$('#dropzone')
           	.get(0)
           	.attachEvent('ondragleave', function(event) { $('#dropzone').css("background-color", "#fff"); }, false);
   	} else if ($.browser.safari) {
   	$('#dropzone')
          .get(0)
          .addEventListener('drop', upload, false);
       	$('#dropzone')
          .get(0)
          .addEventListener('dragover', function(event) { event.returnValue = false; }, false);
       	$('#dropzone')
          .get(0)
          .addEventListener('dragenter', function(event) { $('#dropzone').css("background-color", "#ffc"); }, false);
       	$('#dropzone')
          .get(0)
          .addEventListener('dragleave', function(event) { $('#dropzone').css("background-color", "#fff"); }, false);
}
І створюється обробник upload(), що спрацьовує, коли файл потрапляє у визначену область.
Для передачі данних на сервер Gears використовує стандарт RFC2388. Тому основна частина коду - побудова запиту згідно цього стандарту.
Потрібно зважати на те, що відразу можна перетягувати не один, а декілька файлів. Тому додавати їх у запит потрібно в циклі.

Обробник події onreadystatechange спрацьовує, коли запит завантаження на сервер буде виконаний. Тут буде відбуватися відображення данних у вказаній області. Для цього використовується jquery і довільний серверний скрипт (до нього можна буде додати можливість зміни розміру зображень при необхідності).

І нарешті дані відправляються на сервер, використавуючи метод POST і вказавши url обробника на сервері (/images/upload/).
function upload(event) {
       var data = desktop.getDragData(event, 'application/x-gears-files');
       var boundary = '------multipartformboundary' + (new Date).getTime();
       var dashdash = '--';
       var crlf     = '\r\n';
       /* Build RFC2388 string. */
       var builder = google.gears.factory.create('beta.blobbuilder');
       builder.append(dashdash);
       builder.append(boundary);
       builder.append(crlf);
       for (var i in data.files) {
           var file = data.files[i];
           /* Generate headers. */
           builder.append('Content-Disposition: form-data; name="user_file[]"');
           if (file.name) {
             builder.append('; filename="' + file.name + '"');
           }
           builder.append(crlf);
           builder.append('Content-Type: application/octet-stream');
           builder.append(crlf);
           builder.append(crlf);
           /* Append binary data. */
           builder.append(file.blob);
           builder.append(crlf);
           /* Write boundary. */
           builder.append(dashdash);
           builder.append(boundary);
           builder.append(crlf);
       }
       /* Mark end of the request. */
       builder.append(dashdash);
       builder.append(boundary);
       builder.append(dashdash);
       builder.append(crlf);
       request.upload.onprogress = function() {
       };
       request.onreadystatechange = function() {
           switch(request.readyState) {
               case 4:
                   /* If we got an error display it. */
                   if (request.responseText) {
                       alert(request.responseText);
                   }
                   $("#dropzone").load("/images/");
                   break;
           }
       };
       /* Use Gears to submit the data. */
       request.open("POST", "/images/upload/");
       request.setRequestHeader('content-type', 'multipart/form-data; boundary=' + boundary);
       request.send(builder.getAsBlob());
       /* Prevent FireFox opening the dragged file. */
       if ($.browser.mozilla) {
           event.stopPropagation();
       }
       /* Fix Safari forgetting the background change after drop. */
       $('#dropzone').css("background-color", "#fff");
}
На php обробка файлів на сервері робиться звичайними методами - використовуючи $_FILES та функцію move_uploaded_file().

Використані посилання

Встановлення Google Gears - http://tools.google.com/gears/.

Google Gears API - http://code.google.com/intl/uk-UA/apis/gears/design.html.

Завантаження файлу перетягуванням з допомогою Google Gears - http://www.appelsiini.net/2009/10/drag-and-drop-file-upload-with-google-gears.

Робота з завантаженими на сервер файлами з допомогою php - http://php.net/manual/en/function.move-uploaded-file.php.

Репозиторій файлів з прикладами - http://github.com/tuupola/demo_code/tree/master/gears_upload/.

Приклад, тут можна спробувати це в дії - http://www.appelsiini.net/demo/gears_upload/demo.html.

коментарі:

+1meako 12.11.2009 10:24
Гарна компіляція, веб стає все динамічнішим і функціонально багатшим.

На wave я так розумію, jaquery не використовується? Там якісь свої методи.

Так, і останнє посилання замість працюючого демо, веде на github репозиторій. Чи це натяк: щоб попробувати в дії, треба в себе підняти:)
А отут є справжнє демо
sky 12.11.2009 10:37
ага, дякую. я саме його хотів вставити, але помилився. вже доповнив.
в wave здається не jquery.
SqREL 12.11.2009 10:51
Не можу зрозуміти як його установити на Chromium (Ubuntu)
meako 12.11.2009 17:02
В репозиторіях є? Як нема, то вам сюди

додати коментар: