Решение... Советы... Windows 10

HTML5: загрузка файлов с помощью Drag-and-drop. HTML5: загрузка файлов с помощью Drag-and-drop Работа с загруженными файлами

А этом уроке я покажу вам пример интеграции замечательного плагина Dropzone.js с вашим сайтом на php для загрузки файлов на сервер буквально в несколько строк кода.

Dropzone.JS это замечательная опенсурс библиотека написанная на ванильном JS предоставляющая вам интерфес для загрузки файлов drag and drop с предварительным просмотром файлов.

Для начала скачайте свежую версию библиотеки и её стили:

Затем создайте папку uploads и файлы index.php и upload.php

В качестве файла index php может выступать та часть вашего кода, которая содержит форму добавления материалов на сайт. В моём примере я создам чистую страницу с минимальной разметкой и подключеной библиотекой и стилями Dropzone.js:

Как вы наверное заметили мы создали форму с экшеном upload.php, но не создавали никаких инпутов для прикрепления файлов, и не объявляли enctype формы. В этом нет никакой ошибки, все обрабатывается самой библиотекой DropzoneJS. Все, что нам нужно сделать, это присвоить форме класс dropzone. По умолчанию DropzoneJS находит все формы с данным классом и автоматически потрисовывет свой интерфейс.

Вы пожете открыть на исполнение в браузете страницу index.php и убедиться что библиотека срабатывает как задумано. Вот что у меня получилось:

Теперь давайте создадим обработчик загрузки для которого мы уже создали файл upload.php. Привожу пример моего простейшего кода загрузки:

Работа с загруженными файлами

Для полноценного взаимодействия с вашими фалами, нам осталась добавить возможность манипулировать ими. Для начала нам нужно добавить фрагмент кода для извлечения информации о хранящихся файлах (имя и размер) и вернуть его в формате JSON.

Для этого обновляем файл upload.php до такого вида (вставлено условие else):

  • Функция PHP scandir просматривает папку uploads и возращает массив файлов или значение FALSE елси папка пуста.
  • Перебираем возвращаемое значение из функции scandir и сохраняем в массив $ result. Помните, мы игнорируем "." И "..", поскольку scandir всегда будет возвращать "." И ".." в качестве допустимого содержимого, относящегося к текущему и предыдущему каталогу.
  • Выводим правильные заголовки для разметки JSON, а также конвертируем PHP-массив в JSON-строку, используя функцию json_encode.
  • Теперь пришло время обновить index.php:

    Dropzone.options.myDropzone = { init: function() { thisDropzone = this; $.get("upload.php", function(data) { $.each(data, function(key,value){ var mockFile = { name: value.name, size: value.size }; thisDropzone.options.addedfile.call(thisDropzone, mockFile); thisDropzone.options.thumbnail.call(thisDropzone, mockFile, "uploads/"+value.name); }); }); } };

    Что мы тут накодили? Давайте разбиратся:

  • Увы и ах, мы добавили библиотеку Jquery на нашу страницу. Это на самом деле не необходимость для DropzoneJs. Мы используем только ajax-функцию JQuery $ .get. Вы же на своё усмотрение можете реализовать подобные запросы на vue.js или что вам больше по душе.
  • Мы добавили в форму элемент ID (my-dropzone). Это необходимо, для того что бы передать значения конфигурации в Dropzone. И для этого мы должны иметь уникальный индификатор указывающий на него. Таким образом мы можем настроить библиотеку, присвоив значения Dropzone.options.myDropzone.
  • Инициализируем основную часть редактирования. То, что мы здесь сделали, - передали функцию прослушивания события init Dropzone. Это событие вызывается, когда Dropzone инициализируется.
  • Получаем массив файлов из «upload.php» посредством ajax.
  • Создаём mockFile, используя значения с сервера. MockFile - это просто объекты JavaScript со свойствами имени и размера. Затем мы явно вызываем функции Dropbox и добавляем иконки, чтобы поместить существующие файлы в область загрузки Dropzone и создать их миниатюры.
  • Если вы сделали всё корректно. Загрузите несколько изображений и перезагрузите страницу с формой. Ранее загруженные файлы должны автоматически отображаться в области Dropzone.

    • HTML

    Благодаря нововведениям HTML5 создавать Drag and Drop интерфейсы стало гораздо проще. К сожалению, эти нововведения еще не обладают обширной поддержкой браузеров, но надеюсь в скором времени это изменится (на данный момент работает в Firefox 4+, Chrome и Opera 11.10).

    Разметка Сразу говорю, статья написана больше для новичков, чем для профессионалов. Поэтому некоторые моменты будут описываться очень подробно.

    Для начала, нам необходимо создать HTML файл с таким содержанием:

    Для загрузки, перетащите файл сюда.

    Вся работа у нас будет происходить с контейнером dropZone. Теперь добавим стили для нашего документа (style.css):

    Body { font: 12px Arial, sans-serif; } #dropZone { color: #555; font-size: 18px; text-align: center; width: 400px; padding: 50px 0; margin: 50px auto; background: #eee; border: 1px solid #ccc; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } #dropZone.hover { background: #ddd; border-color: #aaa; } #dropZone.error { background: #faa; border-color: #f00; } #dropZone.drop { background: #afa; border-color: #0f0; }

    В стилях Вы можете заметить три состояния у элемента dropZone: при наведении, если возникает какая-то ошибка и при успешном выполнении.

    Скрипт загрузки Теперь мы приступим к самому интересному - написанию JavaScript кода. Для начала, нам необходимо создать переменные, в одну из которых мы поместим нашу dropZone, а во второй укажем максимальный размер файла.

    $(document).ready(function() { var dropZone = $("#dropZone"), maxFileSize = 1000000; // максимальный размер файла - 1 мб. });

    Дальше мы должны проверить поддерживает ли браузер Drag and Drop, для этого мы будем использовать FileReader функцию. Если браузер не поддерживает Drag and Drop, то внутри элемента dropZone мы напишем «Не поддерживается браузером!» и добавим класс «error».

    If (typeof(window.FileReader) == "undefined") { dropZone.text("Не поддерживается браузером!"); dropZone.addClass("error"); }

    Следующее что мы сделаем это будет анимация эффекта перетаскивания файла на dropZone. Отслеживать эти действия мы будет с помощью событий «ondragover» и «ondragleave». Но, так как эти события не могут быть отслежены у jQuery объекта, нам необходимо обращаться не просто к «dropZone», а к «dropZone».

    DropZone.ondragover = function() { dropZone.addClass("hover"); return false; }; dropZone.ondragleave = function() { dropZone.removeClass("hover"); return false; };

    Теперь нам необходимо написать обработчик события «ondrop» - это событие когда перетянутый файл опустили. В некоторых браузерах при перетягивании файлов в окно браузера они автоматически открываются, что бы такого не произошло нам нужно отменить стандартное поведение браузера. Также нам необходимо убрать класс «hover», и добавить класс «drop».

    DropZone.ondrop = function(event) { event.preventDefault(); dropZone.removeClass("hover"); dropZone.addClass("drop"); };

    Var file = event.dataTransfer.files; if (file.size > maxFileSize) { dropZone.text("Файл слишком большой!"); dropZone.addClass("error"); return false; }

    Теперь нам необходимо написать AJAX запрос отсылающий наш файл в обработчик. Для создания AJAX запроса мы будем использовать объект XMLHttpRequest. Добавим для объекта XMLHttpRequest два обработчика событий: один будет показывать прогресс загрузки файла, а второй результат загрузки. В качестве обработчика укажем файл upload.php.

    Var xhr = new XMLHttpRequest(); xhr.upload.addEventListener("progress", uploadProgress, false); xhr.onreadystatechange = stateChange; xhr.open("POST", "/upload.php"); xhr.setRequestHeader("X-FILE-NAME", file.name); xhr.send(file);

    Теперь займемся функциями прогресса загрузки и результата загрузки. В функции «uploadProgress» нет ничего сложного, лишь простейшая математика.

    Function uploadProgress(event) { var percent = parseInt(event.loaded / event.total * 100); dropZone.text("Загрузка: " + percent + "%"); }

    В функции «stateChange» мы должны проверить завершен ли процесс загрузки, и если да, то необходимо проверить не возникла ли какая-либо ошибка. Код успешного запроса «200», если же код отличается от этого, то это означает, что произошла ошибка.

    Function stateChange(event) { if (event.target.readyState == 4) { if (event.target.status == 200) { dropZone.text("Загрузка успешно завершена!"); } else { dropZone.text("Произошла ошибка!"); dropZone.addClass("error"); } } }

    Написание JavaScript части завершено.

    Серверная часть Все что нам осталось, это написать простейший обработчик, который будет сохранять файл в нужном нам месте. В написании обработчика я не буду сильно углубляться, а лишь приведу небольшой пример на PHP.

    На этом всё, надеюсь статья была полезной для Вас.

    Скачать исходные файлы вы можете

    Для начала, конечно, понадобиться создать элемент, который будет «ловить» файл. Кроме того, поместим в этот элемент span-тег для вывода сообщений о состоянии загрузки и input с типом file , чтобы не ограничивать выбор файла лишь перетаскиванием, а так же дать возможность пользователям выбрать файл нажатием на эту обозначенную область. Конечный вид такой структуры представлен ниже.

    Нажмите сюда или перетащите файл для загрузки.

    CSS к данному HTML коду ничем не примечателен, за исключением оформления поля input :

    #file{ width:100%; height:100%; display:block; position:absolute; top:0; left:0; opacity:0.01; }

    Так же описываем два класса, которые при добавлении к области «захвата» файла будут сигнализировать об успешной загрузке файла или же об ошибке, при возникновении таковой:

    #drop-zone.success{ background-color:#2ecc71; } #drop-zone.error{ background-color:#e74c3c; }

    Теперь можем перейти к написанию «экшена» нашей страницы. Для начала запишем в переменные ссылки на объекты, к которым будем достаточно часто обращаться:

    Var dropZone = document.getElementById("drop-zone"); var msgConteiner = document.querySelector("#drop-zone .text");

    После этого избавимся от событий по умолчанию при попадании курсора в нашу область приема файла следующим образом:

    Var eventClear = function (e) { e.stopPropagation(); e.preventDefault(); } dropZone.addEventListener("dragenter", eventClear, false); dropZone.addEventListener("dragover", eventClear, false);

    DropZone.addEventListener("drop", function (e) { if(!e.dataTransfer.files) return; e.stopPropagation(); e.preventDefault(); sendFile(e.dataTransfer.files); }, false); document.getElementById("file").addEventListener("change", function (e) { sendFile(e.target.files); }, false);

    В обоих случаях событие завершается вызовом функции sendFile , в которую передается полученный от пользователя файл.

    Эта функция отвечает за передачу файла серверу. Ее описание можете увидеть ниже.

    Var sendFile = function(file) { // удаляем классы состояния, которые могли быть добавлены // если пользователь уже пытался что либо загрузить dropZone.classList.remove("success"); dropZone.classList.remove("error"); // делаем проверку при помощи регулярного выражения на тип файла // (в примере только изображения допускаем к загрузке) var re = /(.jpg|.jpeg|.bmp|.gif|.png)$/i; if (!re.exec(file.name)) { msgConteiner.innerHTML = "Недопустимый формат файла!"; dropZone.classList.remove("success"); dropZone.classList.add("error"); } else { var fd = new FormData(); // создание объекта формы fd.append("upfile", file); // добавление файла в форму отправки var xhr = new XMLHttpRequest(); xhr.open("POST", "./upload.php", true); xhr.upload.onprogress = showProgress; xhr.onreadystatechange = statChange; xhr.send(fd); // отправка на сервер } }

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

    Var showProgress = function(e) { if (e.lengthComputable) { // расчет процента загрузки var percent = Math.floor((e.loaded / e.total) * 100); // вывод текущего процента msgConteiner.innerHTML = "Загрузка... ("+ percent +"%)"; } }; var statChange = function (e) { if (e.target.readyState == 4) { // по завершению обработки запроса к серверу if (e.target.status == 200) { // если запрос выполнен успешно msgConteiner.innerHTML = "Загрузка успешно завершена!"; dropZone.classList.remove("error"); dropZone.classList.add("success"); document.getElementById("showUpFile").innerHTML = this.responseText; } else { // иначе msgConteiner.innerHTML = "Произошла ошибка!"; dropZone.classList.remove("success"); dropZone.classList.add("error"); } } }

    Завершающим этапом будет обработка получаемых сервером данных.

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

    В большинстве случаев вам предложат стандартное поле с кнопкой выбора файла с вашего компьютера и/или поле, в котором можно указать URL файла, размещенного где-нибудь в Сети.

    Загрузку файлов с локального компьютера трогать пока не будем, я планирую опубликовать отдельный пост на эту тему, разберем загрузку с удалённого сервера.

    Проблемы начинаются с первого же шага. Даже если вы четко понимаете, где искать URL и хорошо умеете пользоваться инструментами вроде firebug, то всё равно потребуется несколько кликов мышкой чтобы получить нужный адрес. Было бы гораздо удобнее просто перетянуть нужную картинку из одного окна браузера в другое.

    реализации такого интерфейса я покажу в этой статье. Если хотите, можете посмотреть, как он работает на демонстрационной страничке или скачать архив с исходниками.

    Обратите внимание! Данный пример работает только в браузере Google Chrome. По-идее, поддержка всех необходимых технологий есть в Firefox и Safari, но с ними я пока не разбирался.

    В качестве объектов для "перетягивания" я брал в основном картинки с википедии. Было замечено несколько проблем связанных с не латинскими символами в URL картинок, но чтобы не перегружать пример проверками и преобразованиями я их оставил как есть.


    Принцип работы

    Стандарт HTML5 предусматривает поддержку "перетягивания" объектов страницы (Drag and Drop ). Кстати, пример простейшей реализации D&D я уже показывал - Drag & Drop с использованием HTML5. И, кроме того, есть довольно много JavaScript библиотек, реализующих поддержку D&D.

    Но тут важно понимать, что если необходимо "перетягивать" картинки со сторонних ресурсов, то использовать библиотеки не получится. Т.к. вы не сможете добавить свой JS код на чужую страницу. А для того, чтобы загрузить картинку, нам нужно получить её URL, т.е. браузер должен вместе с перетягиваемым объектом передавать и его параметры (например, атрибут src картинки или весь тег img).

    В этом случае мы можем создать на своей странице "приёмник" картинок. Это будет обычный div которому назначен обработчик события drop. Если пользователь "сбросит" картинку над этим div"ом, то будет вызван обработчик и в первом параметре он получит объект, содержащий информацию о перетягиваемой картинке.

    Реализация

    Начнём со страницы нашего приложения.





    Images Upload








    На ней размещены два блока: images - здесь будем показывать загруженные изображения и img_target - на этот блок нужно перетягивать картинки.

    Внизу страницы подключаем библиотеку jQuery и скрипт main.js, который будет отправлять информацию о перетянутых изображениях на сервер.

    Рассмотрим main.js

    $(function() {
    $("#img_target")
    .bind("dragenter", function(event) {
    $(this).addClass("drop_here");
    return false;
    })
    .bind("dragleave", function(event) {
    return false;
    })
    .bind("dragover", function(event) {
    return false;
    })
    .bind("drop", function(event) {
    $(this).removeClass("drop_here");
    var srcRegex = /src=\"([^\s]+)\"/ig;
    var data = event.originalEvent.dataTransfer.getData("text/html");
    var img_data = srcRegex.exec(data);
    $.post("upload.php", {"file_url":img_data}, function(res) {
    var response = eval("(" + res + ")");
    $("#images").append($(""));
    });
    return true;
    });

    Здесь мы назначаем обработчики событиям dragenter, dragleave и dragover. Все они должны просто возвращать false и, чтобы как-то проинформировать пользователя о том, что можно "сбрасывать" картинку, в обработчике dragenter устанавливаем CSS класс drop_here для блока-приёмника.

    Основная часть работы выполняется в обработчике события drop. При возникновении этого события мы читаем информацию о "сброшенном" объекте и "вырезаем" значение атрибута src, т.е. URL картинки (строки 16-18). Информация передается в объекте event.originalEvent.dataTransfer (строка 17).

    Затем формируем обычный AJAX запрос и в качестве параметра передаём ему найденный URL.

    Серверный скрипт (upload.php) получит URL изображения на удалённом сервере и загрузит его. А в ответе на AJAX запрос он отправит новый URL загруженной картинки.

    В свою очередь, обработчик AJAX-запроса создаст тег img и вставит его в блок images. Таким образом, загруженные картинки будут появляться над полем загрузки.

    Рассмотрим upload.php

    define("BASE_URL", "http://localhost/tests/images-upload/");

    function upload_from_url($file_url) {
    $url_segments = explode("/", $file_url);
    $file_name = urldecode(end($url_segments));
    if (false !== $file_name) {
    $file_name_parts = explode(".", $file_name);
    if (in_array(strtolower(end($file_name_parts)), array("jpeg","jpg","png","gif"))) {
    $destination=fopen("upload/".$file_name,"w");
    $source=fopen($file_url,"r");
    $maxsize=300*1024;
    $length=0;
    while (($a=fread($source,1024))&&($length "Не указан URL файла");

    if (isset($_POST["file_url"])) {
    $new_url = upload_from_url($_POST["file_url"]);
    $res = array("file_url" => $new_url);
    }

    echo json_encode($res);

    Принцип работы следующий. Читаем URL картинки и пытаемся её загрузить (строки 29-32).

    Если картинка загружена, сохраняем её в папку upload. Получение картинки с удалённого сервера осуществляется с помощью функций fread. Файл читаем блоками по 1кБ (строки 15-18). Такой подход позволяет прервать загрузку файла, если его размер превышает заданный предел (в данном случае 300кБ).

    После загрузки файла формируем для него URL и отправляем браузеру в формате JSON.

    Как видите, реализовать такой загрузчик несложно. И пользоваться им достаточно удобно. Естественно, основным недостатком является поддержка HTML5 браузерами, точнее её отсутствие

    Тем не менее, если вы создаёте интерфейс для сотрудников какой-нибудь компании, и можете оговорить тип браузера, то HTML5 вполне можно использовать.

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

    Мы будем использовать плагины jQuery File Upload и jQuery Knob для отображения .

    Т.к. форма будет иметь возможность загружать файлы двумя способами, она будет работать даже если drag/drop не поддерживается.

    HTML

    Как обычно, начнем с HTML5 разметки:

    Mini Ajax File Upload Form Drop Here Browse

    В теге нашего документа, подключаем шрифты Google Webfonts, а перед закрытием тега — JavaScript библиотеки: jQuery, jQuery Knob и jQuery File Upload.

    Главный элемент страницы — форма #upload . Внутри нее элемент #drop (принимает файлы с использованием drag&drop) и список, в котором будут отображаться загружаемые файлы. Разметка, которая будет генерироваться для загруженных файлов:

  • Sunset.jpg 145 KB

  • Элемент input будет спрятан с помощью CSS. Он нужен для инициализации плагина jQuery Knob, который будет рисовать файла. Input имеет атрибуты data-* , которые используются для обновления шкалы. Позже, когда мы будет отслеживать прогресс загрузки, мы будем обновлять эти значения для перерисовки шкалы. span будет содержать зеленую галку или красный крест.

    jQuery

    У посетителя будет 2 способа загрузки файла:

    • Перетащить файл в окно браузера (в IE не работает).
    • Нажатием на кнопку browse. Будет имитировано нажатие на спрятанный input и показано системное окно выбора файлов. Обратите внимание, что input имеет параметр multiple параметр, что позволит выбирать много файлов за один раз.

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

    $(function(){ var ul = $("#upload ul"); $("#drop a").click(function(){ // имитация нажатия на поле выбора файла $(this).parent().find("input").click(); }); // инициализация плагина jQuery File Upload $("#upload").fileupload({ // этот элемент будет принимать перетаскиваемые на него файлы dropZone: $("#drop"), // Функция будет вызвана при помещении файла в очередь add: function (e, data) { var tpl = $("

  • "); // вывод имени и размера файла tpl.find("p").text(data.files.name) .append("" + formatFileSize(data.files.size) + ""); data.context = tpl.appendTo(ul); // инициализация плагина jQuery Knob tpl.find("input").knob(); // отслеживание нажатия на иконку отмены tpl.find("span").click(function(){ if(tpl.hasClass("working")){ jqXHR.abort(); } tpl.fadeOut(function(){ tpl.remove(); }); }); // Автоматически загружаем файл при добавлении в очередь var jqXHR = data.submit(); }, progress: function(e, data){ // Вычисление процента загрузки var progress = parseInt(data.loaded / data.total * 100, 10); // обновляем шкалу data.context.find("input").val(progress).change(); if(progress == 100){ data.context.removeClass("working"); } }, fail:function(e, data){ // что-то пошло не так data.context.addClass("error"); } }); $(document).on("drop dragover", function (e) { e.preventDefault(); }); // вспомогательная функция, которая форматирует размер файла function formatFileSize(bytes) { if (typeof bytes !== "number") { return ""; } if (bytes >= 1000000000) { return (bytes / 1000000000).toFixed(2) + " GB"; } if (bytes >= 1000000) { return (bytes / 1000000).toFixed(2) + " MB"; } return (bytes / 1000).toFixed(2) + " KB"; } });

    В jQuery File Upload есть собственный интерфейс, но мы используем базовую версию плагина для создания собственного дизайна интерфейса. И чтобы наш интерфейс работал, мы передаем плагину несколько параметров / колбэков:

    • dropZone – этот параметр содержит jQuery селектор, который будет принимать перетаскиваемые файлы. Файлы, брошенные на него, будут добавлены в очередь загрузки.
    • add – функция вызывается при добавлении файла в очередь загрузки. Она будет генерировать разметку для файлов и вызвать метод data.submit() .
    • progress – Эта функция будет вызываться каждые 100ms (можно изменить). Второй аргумент содержит размер файла и количество загруженных байт. Это позволяет отслеживать прогресс и обновлять шкалу.
    • fail – функция вызывается при возникновении ошибки.
    PHP

    В комплект jQuery File Upload входит также и PHP скрипт для обработки файлов на сервере, но мы напишем свой. Процесс загрузки фалов будет происходить также как и при обычной загрузке, поэтому всю информацию о них мы можем получить из глобального массива $_FILES: