Ранее я писал про отправку форм с использованием ajax’a. Для отправки формы способом, описанным в той статье, необходимо было слепить тело запроса из пар ключ/значение (имя поля / значение) самостоятельно. Кроме того, тем способом нельзя было загрузить файл на сервер, если в форме было расположено поле c type=”file”.
Всего этого можно избежать, если использовать объект FormData. К сожалению, он пока поддерживается далеко не всеми современными браузерами. Его поддерживают только последние, на момент написания статьи, версии Google Сhrome и FireFox.
Объект FormData позволяет составить набор пар ключ/значение для отправки при помощи XMLHttpRequest. Это, в первую очередь, предназначено для отправки данных форм, но вы можете использовать этот объект независимо от форм, тогда передаваемые данные будут в том же формате, что и при обычной отправке формы с enctype=»multipart/form-data».
Создание объекта FormData при помощи HTML формы
Для создания объекта FormData, который содержит данные существующей формы необходимо передать конструктору в качестве параметра эту форму:
var fd = new FormData(form);
Для отправки полученных данных нужно использовать объект XMLHttpRequest.
var form = document.getElementById("form"), xhr = new XMLHttpRequest(); xhr.open("POST", "submitform.php"); xhr.send(new FormData(form));
Вы также можете добавить дополнительные данные к объекту FormData перед их отправкой на сервер.
var form = document.getElementById("form"), fd = new FormData(form); fd.append("serialnumber", serialNumber++); xhr.send(fd);
Отправка форм с помощью объекта FormData
В форме, данные которой отправляются на сервер с иcпользованием объекта FormData также может содержаться и input type=”file”.
Давайте попробуем отправить такую форму, используя FormData. Ограничим максимальный размер загружаемого файла одним мегабайтом.
Сама форма будет выглядеть вот так:
<form action="upload.php" method="post" enctype="multipart/form-data" onsubmit="return sendForm(this, ge('content'))"> <input type="text" placeholder="Ваше имя" name="name" id="name" /> <progress class="pBar" min="0" max="100" value="0">0% complete</progress> <input type="file" name="file" id="file" /> <div align="right"><div id="status"></div> <input type="submit" name="go" id="go" value="Загрузить" /></div> </form>
Про атрибут placeholder текстового поля я писал ранее.
Содержимое файла style.css:
body { background-color: #ececec; font: 62.5% "Trebuchet MS", Tahoma, Arial, sans-serif; line-height: 1.6em; color: #444; font-size: 14px; } #content { background-color: #fff; padding: 30px; margin: 200px auto 0; width: 250px; } input[type=text] { width: 240px; } input[type=file] { width: 250px; } input[type=text], input[type=file] { padding: 5px; font-size: 16px; border: 1px solid #ccc; color: #999; margin-bottom: 20px; } input[type=submit] { background-color: #ccc; border: none; padding: 5px; font-size: 16px; cursor: pointer; } input[type=submit]:hover { background-color: #89bd51; color: #fff; } .pBar { width: 250px; height: 30px; margin-bottom: 20px; } #status { float: left; color: red; } .notSupport { color: red; }
Содержимое файла script.js в котором расположена функция sendForm:
//Избавляем себя от лишнего стука по клаве и сокращаем код function ge(id) { return document.getElementById(id); } // Как только страничка загрузилась window.onload = function () { // проверяем поддерживает ли браузер FormData if(!window.FormData) { /* * если не поддерживает, то выводим * то выводим предупреждение вместо формы */ var div = ge('content'); div.innerHTML = "Ваш браузер не поддерживает объект FormData"; div.className = 'notSupport'; } } function sendForm(form, output) { var data = new FormData(form), /* * Использовать кроссбраузерный способ создания * не имеет смысла, т.к. браузеры для, для которых * XMLHttpRequest (xhr) создаётся по-другому, не поддерживают FormData */ xhr = new XMLHttpRequest(), progressBar = document.querySelector('progress'), goBtn = ge('go'), fileInp = ge('file'), nameInp = ge('name'); if(nameInp.value == '' && fileInp.value == '') { ge('status').innerHTML = 'Заполните поля!'; return false; } else if(nameInp.value == '') { ge('status').innerHTML = 'Введите имя!'; return false } else if(fileInp.value == '') { ge('status').innerHTML = 'Выберите файл!'; return false; } if(fileInp.files[0].size > 1024 * 1024) { // 1 мб ge('status').innerHTML = 'Максимум 1 мб!'; return false; } ge('status').innerHTML = ''; xhr.open('POST', form.action); xhr.onload = function (e) { output.innerHTML = e.currentTarget.responseText; } xhr.upload.onprogress = function (e) { progressBar.value = e.loaded / e.total * 100; } xhr.send(data); return false; }
Скрипт на сервере, который обрабатывает полученные данные, расположен в файле upload.php. Вот его содержимое:
<?php $error = $_FILES['file']['error']; switch($error) { case 0 : $error = 'нет'; break; case 1 : case 2 : $error = 'слишком большой файл'; break; case 3 : $error = 'файл загружен частично'; break; case 4 : $error = 'файл не был загружен'; } ?> Ваше имя: <?=$_POST['name']; ?><br /> Имя файла: <?=$_FILES['file']['name']; ?><br /> Mime type: <?=$_FILES['file']['type']; ?><br /> Ошибки: <?=$error; ?><br /> Размер файла: <?=$_FILES['file']['size']; ?> байт
Этот скрипт просто выводит информацию о залитом файле и текстовое поле (имя пользователя) в доказательство того, что файл действительно был загружен. Вы можете сохранять файл на сервере и возвращать ссылку на его скачивание пользователю, создав, таким образом, файлообменник или прикреплять картинку к сообщению пользователя. Сами думайте, как это использовать. Чтобы избежать проблем с кодировкой используйте UTF-8.
Надеюсь, вскоре этот способ заменит нам костыли с загрузкой файлов с использованием скрытого айфрейма.