Javascript мультизагрузка файлов
Несомненно, существует огромное количество готовых библиотек на Javascript, позволяющих реализовать такую функциональность в несколько строк кода, однако если Вас по какой-то причине они не устраивают, или Вы хотите понять принципы технологии, эта статья может оказаться полезной для Вас.
В этой статье рассмотрим два способа реализации мультиаплоадинга, без перезагрузки страницы. Один из них достаточно кроссбраузерный (ie8+), второй основан на более современных технологиях, реализованных не во всех браузерах (File API), однако дающих некоторые преимущества
Аплоадим через iframe
Довольно простой для реализации способ, имеющий один неприятный недостаток:
При загрузке нескольких файлов, они будут грузиться как одно целое (при ограничивающих настройках сервера может обернуться неприятным геморроем)
Суть этого способа состоит в использовании скрытого iframe, и свойства формы target, позволяющего отправлять данные в фреймы.
Попробуем создать простой iframe загрузчик.
Скрипт upload_test.php - просто распечатывает массив пришедших файлов:
<?php
// если пришли файлы - распечатываем и завершаем работу
if(!empty($_FILES)) {
print_r($_FILES);
die;
}
?>
Файл test_iframe.html - содержит элементарную верстку:
<!Doctype html>
<html>
<head>
<title>test iframe upload!</title>
<script type="text/javascript" src="js/iframe_upload_test.js"></script></span></span>
</head>
<body>
<div>
<input type="file" id="file" name="test-file">
<input type="button" id="send" value="send via iframe!">
</div>
<iframe id="iframe23" name="iframe"></iframe>
</body>
</html>
Скрипт iframe_upload_test.js - самое интересное:
// дожидаемся окончания загрузки страницы
window.onload = function() {
// подвязываем переменные к элементам
var $send_btn = document.getElementById('send'),
$input = document.getElementById('file'),
$iframe = document.getElementById('iframe23'),
form = document.createElement('form'); // здесь создается динамическая форма
document.body.appendChild(form);
// настраиваем форму
form.target = $iframe.name;
form.action = 'upload_test.php';
form.method = 'POST';
form.enctype = 'multipart/form-data';
// вешаем обработчик событий на кнопку отправки
$send_btn.addEventListener('click', function(e) {
e.preventDefault();
// сохраняем соседние и родительские элементы, чтобы потом можно было вернуть файловый инпут
var sibling = $input.nextSibling,
parent = $input.parentNode;
// переносим файловый инпут в форму
form.appendChild($input);
// отправляем форму
form.submit();
// очищаем инпут
$input.value = '';
// возвращаем инпут на прежнее место
if(sibling)
parent.insertBefore($input, sibling);
else
parent.appendChild($input);
});
// вешаем обработчик события onload на iframe
$iframe.addEventListener('load', function(e) {
// получаем содержимое с которым можно делать все что угодно
var server_data = $iframe.contentWindow.document.body.innerHTML;
});
}
Таким образом можно загружать файлы без перезагрузки страницы, и сразу же выводить их пользователю. Для реализации мультизагрузки, достаточно дополнить атрибут name у файлового инпута квадратными скобками ("test-name[]") и выставить аттрибут multiple:
<input type="file" id="file" name="test-file[]" multiple>
Аплоадим через HTML5 File api
Аплоадинг через iframe, это конечно хорошо, однако на данный момент существует гораздо более совершенный способ аплоадинга, заключающийся в использовании новых api пришедших вместе с html5.
Поддержка браузерами этой технологии уже сейчас довольно приличная, поэтому метод с iframe пригодится только в случае поддержки пользователей со старыми браузерами. Принцип состоит в использовании объектов типа FormData, представляющих данные формы, и обычных ajax запросов посредством объектов XMLHttpRequest.
Использование HTML5 File api дает нам весомые преимущества, например, теперь можно легко отслеживать процесс загрузки файла с помощью обработчика события onprogress или вывести список файлов с превью перед загрузкой на сервер, а комбинируя возможности Canvas и File API (например, для масштабирования и наложения цветовых фильтров прямо в браузере клиента), можно добиться очень впечатляющих эффектов.
Рассмотрим тестовый пример (html5_upload_test.js):
function success(data) {
// делаем с ответом сервера что душе угодно,
// например распечатаем его в консоль
document.getElementById('result').innerHTML = data;
}
function error(status) {
// показываем код ошибки
alert('Error: ' + status);
}
window.onload = function() {
// подвязываем переменные к элементам
var $send_btn = document.getElementById('send'),
$input = document.getElementById('file');
$send_btn.addEventListener('click', function(e) {
e.preventDefault();
// проверяем массив файлов в файловом инпуте
if($input.files.length > 0) {
for(var i = 0; i < $input.files.length; i++) {
var formData = new FormData(),
file = $input.files[i],
xhr = new XMLHttpRequest();
// заполняем объект FormData (данные формы)
formData.append($input.name, file);
// готовим ajax запрос
xhr.open('POST', 'upload_test.php');
xhr.setRequestHeader('X-FILE-NAME', file.name);
xhr.onreadystatechange = function(e) {
if(e.target.readyState == 4) {
if(e.target.status == 200) {
// успешно отправили файл
success(e.target.responseText);
return;
}
else {
// произошла ошибка
error(e.status);
}
}
};
xhr.send(formData);
}
}
});
}
Функции success и error - это обработчики ответа сервера соответственно. Вся магия начинается тут
// проверяем массив файлов в файловом инпуте
if($input.files.length > 0) {
Массив files файлового инпута содержит массив файлов, выбранных пользователем для загрузки (объекты типа File), для упаковки файлов в запрос потребуется использовать объекты типа FormData, а также объект XMLHttpRequest для отправки данных на сервер:
for(var i = 0; i < $input.files.length; i++) {
var formData = new FormData(),
file = $input.files[i],
xhr = new XMLHttpRequest();
Вызовом метода append(name, data) мы добавляем элемент к данным формы:
formData.append($input.name, file);
После чего настраиваем объект XMLHttpRequest, для отправки POST запроса к php скрипту upload_test.php:
// готовим ajax запрос
xhr.open('POST', 'upload_test.php');
xhr.setRequestHeader('X-FILE-NAME', file.name);
Для обработки результатов запроса используем обрабтку события onreadystatechange:
xhr.onreadystatechange = function(e) {
if(e.target.readyState == 4) {
if(e.target.status == 200) {
// успешно отправили файл
success(e.target.responseText);
return;
}
else {
// произошла ошибка
error(e.status);
}
}
}
При успешно выполненном запросе (status == 200) вызываем обработчик success, и передаем в него ответ сервера в виде простого текста (e.target.responseText), в обратном случае вызываем обработчик error.
На этой стадии объект XMLHttpRequest полностью сформирован, осталось только отправить запрос и передать объект FormData:
xhr.send(formData);
Таким образом, все выбранные пользователем файлы поочередно отправляются на сервер посредством AJAX запросов.