Blog. Just Blog

Загрузка файлов перетаскиванием в окно браузера

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Web-мастеру и не только | Автор: ManHunter
Загрузка файлов перетаскиванием в окно браузера
Загрузка файлов перетаскиванием в окно браузера

Практически все современные web-сервисы предлагают пользователям такую фичу, как загрузку файлов простым перетаскиванием их в браузер. Это действительно очень удобно, когда надо загрузить сразу несколько файлов, поле загрузки может иметь произвольный вид и форму, т.к. не подвязано на стандартные элементы формы, и еще множество других плюсов. Как же это сделано? Очень просто. Мы уже рассматривали в одной из статей прием и отправку текстовых данных при помощи технологии AJAX, загрузка файлов выполняется не намного сложнее.

Для начала определим какую-нибудь область на HTML-странице, на которую мы будем перетаскивать файлы. Это очень просто:
  1. <div class="drag" id="drag">Drop-zone</div>
В стилях "зону выгрузки" определите как вам нравится. Кроме того, в качестве "зоны выгрузки" может быть, например, картинка или любой другой элемент HTML. Теперь надо установить на этот элемент обработчики событий, соответствующие перетаскиванию файлов. Это ondragenter - заход на элемент курсора мыши при перетаскивании, ondragover - движение мыши над элементом, ondragleave - выход мыши за пределы элемента при перетаскивании или же сам процесс перетаскивания, ondrop - собственно, интересующее нас событие - само перетаскивание файлов на "зону выгрузки". Для установки обработчиков можно пойти по красивому пути MVC, устанавливая их при загрузке страницы, я же для наглядности сделаю inline-обрабточики.
  1. <div class="drag" id="drag"
  2.      ondragenter="dropenter(event);" ondragover="dropenter(event);"
  3.      ondragleave="dropleave();" ondrop="return dodrop(event);" >
  4.      Drop-zone
  5. </div>
Обратите внимание, что обработчикам dropenter и dropleave передаются описания события event, это нужно, чтобы в функции обработки их можно было подавить. Кроме подавления на этот обработчик можно повесить какой-нибудь полезный эффект, например, чтобы "зона выгрузки" меняла цвет при заходе на нее курсора с перетаскиваемыми файлами.
  1. // Эффект при наведении курсора с файлами на зону выгрузки
  2. function dropenter(e) {
  3.     // Подавить событие
  4.     e.stopPropagation();
  5.     e.preventDefault();
  6.     // Визуальный эффект "зоны выгрузки" при заходе на нее курсора
  7.     var tmp=document.getElementById('drag');
  8.     tmp.style.background='#FF0000';
  9.     tmp.innerHTML='Drop your files here';
  10. }
Обработчик dropleave может отсутствовать, он нужен только если используется визуальный эффект при заходе курсора. При выходе курсора за пределы "зоны выгрузки" она вновь принимает свой первоначальный вид. Подавлять это событие в обработчике также не надо.
  1. // Эффект при отпускании файлов или выходе из зоны выгрузки
  2. function dropleave() {
  3.     // Привести "зону выгрузки" в первоначальный вид
  4.     var tmp=document.getElementById('drag');
  5.     tmp.style.background='#DDDDDD';
  6.     tmp.innerHTML='Drop-zone';
  7. }
Теперь первый из главных обработчиков - загрузка файлов после их "броска" на "зону выгрузки". Этот вариант обработчика кроссбраузерный, он поддерживает не только браузеры на движке Mozilla и WebKit, но также Opera и Internet Explorer. После передачи всех файлов AJAX-загрузчику, обработчик должен обязательно подавить событие, иначе все файлы вместо загрузки будут открыты как страницы. Ну или еще как-нибудь, в зависимости от типа браузера. В этом обработчике можно выполнять предварительную проверку на размер файла и его расширение, чтобы сэкономить время и ресурсы серверной стороны. Некоторые браузеры передают в обработчик отдельным полем MIME-тип загружаемых файлов, но ориентироваться на эту информацию при проверке не стоит. Равно как и не надо целиком доверять проверке на стороне клиента, она сделана исключительно для удобства пользователей, но не как защита от злоумышленников. Серверная обработка полученных данных остается всеобъемлющей.
  1. // Проверка и отправка файлов на загрузку
  2. function dodrop(e) {
  3.     var dt e.dataTransfer;
  4.     if(!dt && !dt.files) { return false ; }
  5.  
  6.     // Получить список загружаемых файлов
  7.     var files dt.files;
  8.  
  9.     // Fix для Internet Explorer
  10.     dt.dropEffect="copy";
  11.  
  12.     // Загрузить файлы по очереди, проверив их размер
  13.     for (var 0files.lengthi++) {
  14.         if (files[i].size<15000000) { 
  15.             // Отправить файл в AJAX-загрузчик
  16.             ajax_upload(files[i]);
  17.         }
  18.         else {
  19.             alert('Размер файла превышает допустимое значение');
  20.         }
  21.     }
  22.  
  23.     // Подавить событие перетаскивания файла
  24.     e.stopPropagation();
  25.     e.preventDefault();
  26.     return false;
  27. }
Второй главный обработчик - отправка файлов на сервер при помощи AJAX. Если вы внимательно читали статью про технологию AJAX, то многое из кода обработчика будет вам знакомым. Это такое же создание объекта XMLHttpRequest и обработчик событий onreadystatechange. Разница только в заголовках, отправляемых на сервер. Кроме того, при загрузке файлов у объекта XMLHttpRequest появляется еще одно событие progress, которое можно отслеживать - общий размер данных и размер уже переданных данных. На этом мы можем построить классный индикатор загрузки, наглядно показывающий реальный процент переданных данных. На странице надо будет создать область, в которой будет отображаться индикаторы загрузки. Для каждого файла в эту область будет добавляться div с именем файла и фоновым рисунком, смещение которого и будет обозначать процент загрузки. Простейший CSS и манипуляции с ним на JavaScript.
  1. // AJAX-загрузчик файлов
  2. function ajax_upload(file) {
  3.     // Mozilla, Safari, Opera, Chrome
  4.     if (window.XMLHttpRequest) {
  5.         var http_request = new XMLHttpRequest();
  6.     }
  7.     // Internet Explorer
  8.     else if (window.ActiveXObject) {
  9.         try {
  10.             http_request = new ActiveXObject("Msxml2.XMLHTTP");
  11.         }
  12.         catch (e) {
  13.             try {
  14.                 http_request = new ActiveXObject("Microsoft.XMLHTTP");
  15.             }
  16.             catch (e) {
  17.                 // Браузер не поддерживает эту технологию
  18.                 return false;
  19.             }
  20.         }
  21.     }
  22.     else {
  23.         // Браузер не поддерживает эту технологию
  24.         return false;
  25.     }
  26.     var name file.fileName || file.name;
  27.  
  28.     // Добавить для файла новую полосу-индикатор загрузки
  29.     var tmp=document.getElementById('upload_overall');
  30.     var new_div document.createElement("div");
  31.     new_div.className='percent_div';
  32.     tmp.appendChild(new_div);
  33.  
  34.     // Обработчик прогресса загрузки
  35.     // Полный размер файла - event.total, загружено - event.loaded
  36.     http_request.upload.addEventListener('progress', function(event) {
  37.         var percent Math.ceil(event.loaded event.total 100);
  38.         var back=Math.ceil((100-percent)*6);
  39.         new_div.style.backgroundPosition='-'+back+'px 0px';
  40.         new_div.innerHTML=(name ': ' percent '%');
  41.     }, false);
  42.  
  43.     // Отправить файл на загрузку
  44.     http_request.open('POST''upload.php?fname='+name,true);
  45.     http_request.setRequestHeader("Referer"location.href);
  46.     http_request.setRequestHeader("X-Requested-With""XMLHttpRequest");
  47.     http_request.setRequestHeader("X-File-Name"encodeURIComponent(name));
  48.     http_request.setRequestHeader("Content-Type""application/octet-stream");
  49.     http_request.onreadystatechange=ajax_callback(http_request,new_div,name);
  50.     http_request.send(file);
  51. }
Финиш уже близок. Переходим к callback-обработчику изменений статуса XMLHttpRequest при загрузке файлов. В него добавлены проверки некоторых параметров, которые должна вернуть серверная сторона в случае успешной или неуспешной загрузки файла. Если вернулся JavaScript, то он выполняется, так сервер может сигнализировать в браузер о том, что файл имеет неправильный формат, размер, или какие-нибудь иные сообщения об ошибке. Иначе индикатор загрузки соответствующего файла принудительно ставится на 100%, это приходится учитывать при загрузке очень маленьких файлов, когда onreadystatechange отрабатывает раньше чем progress.
  1. // Callback-фунция для отработки AJAX
  2. function ajax_callback(http_requestobjname) {
  3.     return function() {
  4.         if (http_request.readyState == 4) {
  5.             if (http_request.status==200) {
  6.                 // Вернулся javascript
  7.                 if (http_request.getResponseHeader("Content-Type")
  8.                     .indexOf("application/x-javascript")>=0) {
  9.                     eval(http_request.responseText);
  10.                 }
  11.                 // Файл загружен успешно
  12.                 else {
  13.                     obj.style.backgroundPosition='0px 0px';
  14.                     obj.innerHTML=(name ': 100%');
  15.                 }
  16.             }
  17.             else {
  18.                 // Ошибка загрузки файла
  19.             }
  20.         }
  21.     }
  22. }
На серверной части прием файла обрабатывается следующим образом: командой fopen("php://input", "rb") открывается входящий поток и из него читается содержимое загружаемого файла. Имя файла передается в параметре GET fname. Ну а куда записывать прочитанные данные - это уже на ваше усмотрение. После загрузки файла серверный скрипт должен вернуть какой-нибудь текстовый код, означающий успешную загрузку например, "OK", или отправить соответствующие заголовки и передать данные через сформированный скрипт JavaScript. Также никто не мешает заменить вывод серверного скрипта на JSON или какой-нибудь более привычный вам формат, модифицировать клиентские скрипты не составит большого труда.

На этом с загрузкой файлов все. Готовый пример такого загрузчика файлов вы можете посмотреть на демонстрационной странице. Загруженные файлы, по понятной причине, никуда не сохраняются. Ограничение размера загружаемого файла - 15 мегабайт.

Поделиться ссылкой ВКонтакте
Просмотров: 25784 | Комментариев: 17

Метки: JavaScript, AJAX, PHP
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Александр (07.12.2018 в 16:08):
Спасибо! Отлично изложено! По вашему примеру сделал загрузчик на работе, всё пашет как часы! Правда ванильный ajax заменил на axios и логика посерьёзнее, но за основу ваш пример. Ещё раз спасибо что не поленились опубликовать!
ManHunter (23.02.2016 в 20:57):
А кроссбраузерность? multiple только для очень современных браузеров:
http://caniuse.com/#feat=input-file-multiple
DiPrm (23.02.2016 в 17:03):
Аналогично раньше думал, что только с помошью flash, но, как оказалось, HTML5 дает такие возможности, добавляя атрибут multiple в поле для загрузки файлов input, что позволяет выбирать несколько файлов и сохранять их в виде массива

В итоге получился такой рабочий пример, как можно адаптировать:

<input type="file" multiple="multiple" onchange="uploadIt(this)">


function uploads (th) {
 
var file = th.files;
  for (i=0; i<file.length; i++){
   
      if (file[i]) {
         ajax_upload(file[i]);
      }             
             }
return false; 
}
ManHunter (20.02.2016 в 16:00):
Это делается через flash или java. Обычными средствами браузера множественный выбор файлов невозможен, так что адаптировать тут нечего.
DiPrm (20.02.2016 в 15:16):
Спасибо за статью! Интересно было бы почитать про мультизагрузчик через стандартное поле выбора файлов, который также часто встречается последнее время. На сколько сложно адаптировать данный пример под эту задачу?
ManHunter (18.12.2015 в 19:09):
Ты забыл добавить ".. да побыстрее!!!"
Сперва тон смени, чепушила, потом попробуй попросить еще раз.
incognitozz (18.12.2015 в 18:20):
Ты полный исходник с PHP выложи. Написал статью, приведи пример в исходниках, чтобы не только профи знали как это все делается, но и делетанты...
ManHunter (11.12.2014 в 17:29):
Чтобы сервер знал, какое имя файла ему передается. На PHP, например, он будет доступен в переменной $_REQUEST['HTTP_X_FILE_NAME']. Это самодельный заголовок, вполне можно обойтись и без него. В примере имя файла дублируется через GET.
Eblinkoff (11.12.2014 в 17:22):
А вот ещё, скажите пожалуйста, что это за заголовок X-File-Name и зачем он?
ManHunter (09.12.2014 в 15:55):
Именно так, без JS работать не будет. Браузер или тут же отдаст файл на "скачивание" обратно, или откроет как текстовый/html документ, если формат позволяет.
Eblinkoff (09.12.2014 в 15:54):
Да, она, проклятая. То есть получается кроссбраузерно, только должен быть включён js... Здорово, спасибо Вам за доступный для понимания пример
ManHunter (08.12.2014 в 22:15):
Статью целиком прочитать гордость не позволяет?
Eblinkoff (08.12.2014 в 20:43):
Хотелось бы узнать с какими браузерами совместим этот код...
ManHunter (23.09.2013 в 13:54):
Ассемблер для души, JS / PHP / SQL для хлеба насущного.
Андрей (23.09.2013 в 13:49):
Занятно наблюдать, как у одного человека мирно уживаются JavaScript и "Образ мышления: Assembler".

Невольно AJAX (по высокоуровневости и по "// Internet Explorer" ) ассоциировался с Вавилонской башней.
ManHunter (23.09.2013 в 10:44):
Не уловил шутку юмора про Вавилон.
Андрей (23.09.2013 в 10:21):
При подобных способах загрузки, как у юзера, у меня всегда возникает вопрос: как будет осуществлена загрузка, параллельно или поочерёдно.

По логике параллельная загрузка может сократить время загрузки (предположительно upload канал будет использован по максимуму). С другой стороны смущает, что при дропе 1000 файлов, "пока они все не загрузятся" результата загрузки видно не будет.
Почему именно параллельная загрузка 6 файлов мне непонятно.

Спасибо за статью. Статья интересная. Хоть и Вавилон, но написано ясно,как-что происходит в клиентской части разобраться можно.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2025
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.005 сек.) / Память: 4.5 Mb
Наверх