Использование объекта FormData для загрузки файлов

Кому нужны лицензии на CS Yazzle — на сайте webmasters.ru проводится CS Yazzle — раздача лицензий.

Ранее я писал про отправку форм с использованием ajax’a. Для отправки формы способом, описанным в той статье, необходимо было слепить тело запроса из пар ключ/значение (имя поля / значение) самостоятельно. Кроме того, тем способом нельзя было загрузить файл на сервер, если в форме было расположено поле c type=”file”.

Всего этого можно избежать, если использовать объект FormData. К сожалению, он пока поддерживается далеко не всеми современными браузерами. Его поддерживают только последние, на момент написания статьи, версии Google Сhrome и FireFox.

Посмотреть демо

Объект FormData позволяет составить набор пар ключ/значение для отправки при помощи XMLHttpRequest. Это, в первую очередь, предназначено для отправки данных форм, но вы можете использовать этот объект независимо от форм, тогда передаваемые данные будут в том же формате, что и при обычной отправке формы с enctype=»multipart/form-data».

Создание объекта FormData при помощи HTML формы

Для создания объекта FormData, который содержит данные существующей формы необходимо передать конструктору в качестве параметра эту форму:

var fd = new FormData(form);

Для отправки полученных данных нужно использовать объект XMLHttpRequest.

1
2
3
4
var form = document.getElementById("form"),
  xhr = new XMLHttpRequest();
xhr.open("POST", "submitform.php");
xhr.send(new FormData(form));

Вы также можете добавить дополнительные данные к объекту FormData перед их отправкой на сервер.

1
2
3
4
var form = document.getElementById("form"),
  fd = new FormData(form);
fd.append("serialnumber", serialNumber++);
xhr.send(fd);

Отправка форм с помощью объекта FormData

В форме, данные которой отправляются на сервер с иcпользованием объекта FormData также может содержаться и input type=”file”.

Давайте попробуем отправить такую форму, используя FormData. Ограничим максимальный размер загружаемого файла одним мегабайтом.

Сама форма будет выглядеть вот так:

1
2
3
4
5
6
7
8
9
<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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//Избавляем себя от лишнего стука по клаве и сокращаем код
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. Вот его содержимое:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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.

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

Посмотреть демо | Скачать исходники