Загрузка файлов с помощью AJAX
Russian (Pусский) translation by Marat Amerov (you can also view the original English article)
Кажется, я не могу дойти до конца интересных возможностей, которые вы можете применить с появляющимися веб-технологиями. Сегодня я покажу вам, как сделать что-то, что до последнего времени было почти беспрецедентным: загрузка файлов через AJAX.
О, конечно, были хаки; но если вы похожи на меня и чувствуете себя грязными каждый раз, когда вы вводите iframe
, вам это очень понравится. Присоединяйтесь ко мне!
Ищете быстрое решение?
Если вы ищете быстрое решение, на Envato Market имеется большая коллекция скриптов и приложений для загрузки файлов.
Этот HTML5 загрузчик файлов особенно примечателен - вы можете легко добавлять файлы, перетаскивая их или кликая. Все файлы будут загружены через AJAX или могут быть добавлены в форму, и файлы могут быть переименованы перед загрузкой. Отличное, быстрое решение, если это то, что вы ищете!



У нас имеются плохие новости?
Это не работает во всех браузерах. Однако с некоторым прогрессивным улучшением (или каким бы то ни было текущим модным словом) у нас будет страница загрузки, которая будет работать даже в IE6 (хотя и без черт AJAX).
Наша AJAX-загрузка будет работать до тех пор, пока доступен
FormData
; в противном случае пользователь получит обычную загрузку.
В нашем проекте есть три основных компонента.
-
multiple
атрибуты элементаinput
файла. - Объект
FileReader
из нового API файлов. - Объект
FormData
из XMLHttpRequest2.
Мы используем атрибут multiple
, чтобы позволить пользователю выбирать несколько файлов для загрузки (многократная загрузка файлов будет работать нормально, даже если FormData
недоступен). Как вы увидите, FileReader
позволяет нам показывать миниатюры загружаемых файлов (мы будем ожидать изображения).
Ни одна из наших трех функций не работает в IE9, поэтому все пользователи IE получат нормальную загрузку (хотя я понимаю, что поддержка `FileReader` доступна в IE10 Dev Preview 2). FileReader
не работает в последней версии Safari (5.1), поэтому они не получат миниатюры, но они получат загрузку AJAX и подтверждение. Наконец, Opera 10.50 поддерживает FileReader
, но не поддерживает FormData
, поэтому он получит миниатюры, но обычную загрузку.
Чтобы продолжить путь, давайте приступим к кодированию!
Шаг 1: Разметка и стилизация
Начнем с некоторой базовой разметки и стилизации. Конечно, это не основная часть этого урока, я не буду относиться к вам как к новичку.
HTML-код
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>HTML5 File API</title> <link rel="stylesheet" href="style.css" /> </head> <body> <div id="main"> <h1>Upload Your Images</h1> <form method="post" enctype="multipart/form-data" action="upload.php"> <input type="file" name="images" id="images" multiple /> <button type="submit" id="btn">Upload Files!</button> </form> <div id="response"></div> <ul id="image-list"> </ul> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script> <script src="upload.js"></script> </body> </html>
Довольно просто, не так ли? У нас есть форма, в которая отправляется post'ом на upload.php
, который мы рассмотрим через секунду, и один элемент ввода, типа file
. Обратите внимание, что он имеет boolean-атрибут multiple
, который позволяет пользователю одновременно выбирать несколько файлов.
Это действительно все, что нужно было здесь увидеть. Давайте двигаться дальше.
CSS
body { font: 14px/1.5 helvetica-neue, helvetica, arial, san-serif; padding:10px; } h1 { margin-top:0; } #main { width: 300px; margin:auto; background: #ececec; padding: 20px; border: 1px solid #ccc; } #image-list { list-style:none; margin:0; padding:0; } #image-list li { background: #fff; border: 1px solid #ccc; text-align:center; padding:20px; margin-bottom:19px; } #image-list li img { width: 258px; vertical-align: middle; border:1px solid #474747; }
Здесь нет абсолютно никаких особенностей.
Шаг 2: PHP
Мы также должны иметь возможность обрабатывать загрузки файлов на стороне сервера, поэтому давайте рассмотрим это далее.
upload.php
<?php foreach ($_FILES["images"]["error"] as $key => $error) { if ($error == UPLOAD_ERR_OK) { $name = $_FILES["images"]["name"][$key]; move_uploaded_file( $_FILES["images"]["tmp_name"][$key], "uploads/" . $_FILES['images']['name'][$key]); } } echo "<h2>Successfully Uploaded Images</h2>";
Имейте в виду, что это были первые строки на PHP, которые я написал с легкостью первый год (я парень из Ruby). Вероятно, вам стоит немного позаботиться о безопасности; однако мы просто следим за тем, чтобы не было ошибок при загрузке. Мы используем встроенную функцию move_uploaded_file
, чтобы переместить файл в папку uploads
. Не забудьте убедиться, что папка доступна для записи.
Итак, прямо сейчас у нас должна быть рабочая форма загрузки. Вы выбираете изображение (несколько, если хотите, и ваш браузер это позволяет), нажмите кнопку “Upload Files!”, и вы получите сообщение “Successfully Uploaded Images.”
Вот как выглядит наш мини-проект:



Но, сейчас 2011 год: мы хотим большего. Вы заметите, что мы связали jQuery и файл upload.js
. Давайте рассмотрим это подробней.
Шаг 3: JavaScript
Давайте не будем терять время: здесь начнем!
(function () { var input = document.getElementById("images"), formdata = false; if (window.FormData) { formdata = new FormData(); document.getElementById("btn").style.display = "none"; } }();
Вот с чего мы начинаем. Мы создаем две переменные: input
- это наш элемент ввода файла; formdata
будет использоваться для отправки изображений на сервер, если браузер поддерживает это. Мы инициализируем его значением false
, а затем проверяем, поддерживает ли браузер FormData
; Если это так, мы создаем новый объект FormData
. Кроме того, если мы можем отправить изображения с помощью AJAX, нам не нужна кнопка “Upload Images!”, Поэтому мы можем скрыть ее. Почему нам это не нужно? Ну, мы собираемся автоматически загружать изображения сразу после того, как пользователь их выбирает.
Остальная часть JavaScript войдет в вашу анонимную функцию. Затем мы создадим небольшую вспомогательную функцию, которая будет отображать изображения в браузере:
function showUploadedItem (source) { var list = document.getElementById("image-list"), li = document.createElement("li"), img = document.createElement("img"); img.src = source; li.appendChild(img); list.appendChild(li); }
Функция принимает один параметр: источник изображения (мы увидим, как его получать). Затем мы просто находим список в нашей разметке и создаем элемент списка и изображение. Мы установили источник изображения в полученный нами источник, поместили изображение в элемент списка и поместили элемент списка в список. Вуаля!
Затем мы должны фактически отображать изображения, отображать их и загружать. Как мы уже говорили, мы это сделаем, когда событие onchange
будет запущено на входном элементе.
if (input.addEventListener) { input.addEventListener("change", function (evt) { var i = 0, len = this.files.length, img, reader, file; document.getElementById("response").innerHTML = "Uploading . . ." for ( ; i < len; i++ ) { file = this.files[i]; if (!!file.type.match(/image.*/)) { } } }, false); }
Нам не нужно беспокоиться о проприетарной модели событий IE, потому что IE9 + поддерживает стандартную функцию addEventListener.
Там доступно больше возможностей, но давайте начнем с этого. Во-первых, нам не нужно беспокоиться о проприетарной модели событий IE, потому что IE9 + поддерживает стандартную функцию addEventListener
(а IE9 и ниже не поддерживают новые функции).
Итак, что мы хотим делать, когда пользователь выбрал файлы? Во-первых, мы создаем несколько переменных. Единственный важный момент сейчас - это len = this.files.length
. Файлы, которые пользователь выбрал, будут доступны из объекта this.files
. Прямо сейчас, нас беспокоит только свойство length
, поэтому мы можем перебирать файлы ...
... это именно то, что мы делаем дальше. Внутри нашего цикла мы устанавливаем текущий файл в file
для удобства доступа. Следующее, что мы делаем, это подтверждение того, что файл является изображением. Мы можем сделать это, сравнивая свойство type
с регулярным выражением. Мы ищем тип, начинающийся с "image", за которым следует что-либо. (Двойной знак восклицания впереди просто преобразует результат в булев тип.)
Итак, что нам делать, если у нас есть изображение на руках?
if ( window.FileReader ) { reader = new FileReader(); reader.onloadend = function (e) { showUploadedItem(e.target.result); }; reader.readAsDataURL(file); } if (formdata) { formdata.append("images[]", file); }
Мы проверяем, поддерживает ли браузер создание объектов FileReader
. Если это произойдет, мы его создадим.
Вот как мы используем объект FileReader
: мы собираемся передать наш объект file
методу reader.readAsDataURL
. Это создает URL-адрес для загруженного изображения. Однако это не работает так, как вы ожидали. URL-адрес данных не возвращается из функции. Вместо этого URL-адрес данных будет частью объекта события.
Имея это в виду, нам нужно зарегистрировать функцию на событие reader.onloadend
. Эта функция принимает объект события, с помощью которого мы получаем URL-адрес данных: он находится на e.target.result
(да, e.target
является объектом reader
, но у меня были проблемы при использовании reader
вместо e.target
внутри этой функции) , Мы просто передадим этот URL-адрес нашей функции showUploadedItem
(которую мы написали выше).
Затем мы проверяем объект formdata
. Помните, что если браузер поддерживает FormData
, formdata
будет объектом FormData
; в противном случае оно будет false
. Итак, если у нас есть объект FormData
, мы будем вызывать метод append
. Цель объекта FormData
- хранить значения, которые вы отправляете через форму; поэтому метод append
просто берет ключ и значение. В нашем случае наш ключ - images[]
; добавив квадратные скобки к концу, мы проверяем каждый раз, что append
добавляет другое значение, мы фактически добавляем его к этому массиву, а не переписываем свойство image
.
Мы почти закончили. В нашем цикле for мы отобразили каждое изображение для пользователя и добавили его в объект formdata
. Теперь нам просто нужно загрузить изображения. Снаружи цикла for
, вот последний фрагмент нашего пазла:
if (formdata) { $.ajax({ url: "upload.php", type: "POST", data: formdata, processData: false, contentType: false, success: function (res) { document.getElementById("response").innerHTML = res; } }); }
Опять же, мы должны убедиться, что у нас есть поддержка FormData
; если мы этого не сделаем, кнопка “Upload Files!” будет видна, и пользователь загрузит фотографии. Однако, если у нас есть поддержка FormData
, мы позаботимся о загрузке через AJAX. Мы используем jQuery для обработки всех особенностей AJAX во всех браузерах.
Вероятно, вы знакомы с методом $.ajax
jQuery: вы передаете ему объект options. Свойства url
, type
и success
обязательны. Свойством data
является наш объект formdata
. Обратите внимание на свойства processData
и contentType
. Согласно документации jQuery, processData
по умолчанию является true
и будет обрабатывать и преобразовывать данные в строку запроса. Мы не хотим этого делать, поэтому мы устанавливаем значение false
. Мы также устанавливаем contentType
в false
, чтобы убедиться, что данные попадают на сервер, как мы и ожидаем.
Вот и все. Теперь, когда пользователь загружает страницу, они видят это:



И после того, как они выберут изображения, они увидят это:



Так же были загружены изображения:

Заключение
Загрузка файлов через AJAX довольно крутая, и здорово, что эти новые технологии поддерживают это без необходимости многословных хаков. Если у вас есть какие-либо вопросы о том, что мы здесь сделали, напишите это в комментариях! Большое вам спасибо за чтение!
И если вам по-прежнему нужна помощь в этой или любой другой проблеме с кодингом, вы можете найти решение в Envato Studio.