S.Tominoff

Fullstack JavaScript разработчик

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 запросов.