В этой заметке вы узнаете, как реализовать AJAX загрузку файлов на сервер с использованием jQuery. Это не так уж сложно!

Не знаю точно, но что-то мне подсказывает, что до появления jQuery загрузка файлов на сервер по AJAX технологии была чем-то очень непонятным, а значит крайне сложным. Но сегодня с появлением jQuery даже не обладающий опытом веб-мастер может сделать это без особых усилий. Однако, так или иначе, разобраться все же придется. И сейчас я попробую очень коротко и понятно объяснить вам, как это делается, а чтобы проще было воспринимать, урок содержит только нужное и разбит на шаги.

Смотрите также:

Замечу заранее, что эта статья вряд ли поможет, если вы совсем плохо разбираетесь в jQuery и PHP, базовые знания обязательны. И, пожалуй, обязательно иметь хоть какой-то опыт в загрузке файлов (картинок) на сервер с обычной HTML формы, по крайней мере нужно представлять как это работает.

Ну, меньше слов, приступим!

Для начала предположим, что у нас есть такой HTML код: поле и кнопка загрузки:

<input type="file" multiple="multiple" accept=".txt,image/*">
<a href="#" class="submit button">Загрузить файлы</a>
<div class="ajax-respond"></div>

1. Получение данных файла из поля file

Первое что нам нужно сделать — это получить данные input поля при добавлении в него файла(ов). Для этого прикрепим к событию change свою функцию, которая установит данные файла:

// Переменная куда будут располагаться данные файлов

var files;

// Вешаем функцию на событие
// Получим данные файлов и добавим их в переменную

$('input[type=file]').change(function(){
	files = this.files;
});

Этот код сохранит данные поля type="file" в переменную files, с которой мы будем работать дальше.

2. Загружаем файлы по клику

Теперь, нам нужно повесить событие клика на кнопку "Загрузить файлы". Тут и будет посылаться AJAX запрос с данными файлов.

Создадим функцию, повесим ее на событие click и отправим AJAX запрос с данными файлов. Этот запрос отличается от обычного AJAX запроса, и тут не подходит обычная отправка POST данных:

// Вешаем функцию ан событие click и отправляем AJAX запрос с данными файлов

$('.submit.button').click(function( event ){
	event.stopPropagation(); // Остановка происходящего
	event.preventDefault();  // Полная остановка происходящего

	// Создадим данные формы и добавим в них данные файлов из files

	var data = new FormData();
	$.each( files, function( key, value ){
		data.append( key, value );
	});

	// Отправляем запрос

	$.ajax({
		url: './submit.php?uploadfiles',
		type: 'POST',
		data: data,
		cache: false,
		dataType: 'json',
		processData: false, // Не обрабатываем файлы (Don't process the files)
		contentType: false, // Так jQuery скажет серверу что это строковой запрос
		success: function( respond, textStatus, jqXHR ){

			// Если все ОК

			if( typeof respond.error === 'undefined' ){
				// Файлы успешно загружены, делаем что нибудь здесь

				// выведем пути к загруженным файлам в блок '.ajax-respond'

				var files_path = respond.files;
				var html = '';
				$.each( files_path, function( key, val ){ html += val +'<br>'; } )
				$('.ajax-respond').html( html );
			}
			else{
				console.log('ОШИБКИ ОТВЕТА сервера: ' + respond.error );
			}
		},
		error: function( jqXHR, textStatus, errorThrown ){
			console.log('ОШИБКИ AJAX запроса: ' + textStatus );
		}
	});

});

Что делает функция? Создает новый объект new formData(), добавляет в него данные файлов из массива files. Затем этот объект данных формы передается в AJAX запрос. 2 параметра нужно установить в false обязательно:

  • processData — потому что jQuery будет конвертировать массив files в строку, и сервер не сможет получить данные.
  • contentType — потому что дефолтные установки jQuery равны application/x-www-form-urlencoded, что не предусматривает отправку файлов. А еще, если установить этот параметр в multipart/form-data, похоже это ничего не даст.

3. Загрузка файлов на сервер

Чтобы наглядно показать, как обрабатывать отправленный во втором пункте запрос, приведу простой php-скрипт, без всяких проверок.

Создадим файл submit.php и добавим в него этот код (предполагается что submit.php лежит в той же папке, где и файл, с которого отправляется AJAX запрос):

<?php

// Здесь нужно сделать все проверки передаваемых файлов и вывести ошибки если нужно

// Переменная ответа

$data = array();

if( isset( $_GET['uploadfiles'] ) ){
	$error = false;
	$files = array();

	$uploaddir = './uploads/'; // . - текущая папка где находится submit.php

	// Создадим папку если её нет

	if( ! is_dir( $uploaddir ) ) mkdir( $uploaddir, 0777 );

	// переместим файлы из временной директории в указанную
	foreach( $_FILES as $file ){
		if( move_uploaded_file( $file['tmp_name'], $uploaddir . basename($file['name']) ) ){
			$files[] = realpath( $uploaddir . $file['name'] );
		}
		else{
			$error = true;
		}
	}

	$data = $error ? array('error' => 'Ошибка загрузки файлов.') : array('files' => $files );

	echo json_encode( $data );
}

Не используйте этот код напрямую! Пишите свой!

Вот и все.

Заключение

Эта статья только обучает технологии загрузки файлов с помощью AJAX. В действительности, вам нужно проверить форматы принимаемых файлов, их размер и уведомить пользователей, что произошла загрузка файла.

Чтобы не собирать весь вышеописанный код вручную, скачайте вот этот архив: ajax-file-upload.zip. Загрузите его содержимое на ваш php сервер, зайдите в паку из архива, и попробуйте загрузить файл. Вы увидите, как все это работает, сможете "пошаманить" над кодом и разобраться подробнее в реальных условиях...

Также, рекомендую к прочтению статью о базовых знаниях для создания AJAX запросов в WordPress:

Ajax в WordPress

Знания из этой статьи вам очень пригодятся при создании AJAX загрузки файлов под WordPress.

Автор: Тимур Камаев

Вам понравился материал?

Добавить комментарий

Такой e-mail уже зарегистрирован. Воспользуйтесь формой входа или введите другой.

Вы ввели некорректные логин или пароль

Извините, для комментирования необходимо войти.

43 комментария

сначала новые
по рейтингу сначала новые по хронологии

Добрый вечер господа. Код у меня почему то не заработал. а именно

var data = new FormData();
$.each( files, function( key, value ){
data.append(key,value);
при console.log(data) пустой объект

Алексей

В общем получилось так, что изменил dataType: на HTML, внес небольший правки и заработало!

Алексей

Спасибо за код, не первый раз его использую. Но в последний раз почему то data.append( key, value ); никак работать не хочет. var data = new FormData();
$.each( files, function( key, value ){
console.log(key+';'+value);
data.append( key, value );
});
console.log(data);
В консоль выводит все файлы - console.log(key+';'+value); но вот data - пустой... ошибки js не выдает....

Уважаемые!
Оберните код в форму, вместо click(function используйте submit(function т.е. событие на форму, а не на кнопку. Далее var form_data = new FormData($(this).get(0)); и передавайте в form_data все параметры формы, в том числе и прикрепленный файл.
$('#form').submit(function(){
var form_data = new FormData($(this).get(0));
$.ajax({
url: './submit.php?uploadfiles',
type: 'POST',
data: form_data,
cache: false,
dataType: 'json',
processData: false, // Не обрабатываем файлы (Don't process the files)
contentType: false,
success: function(){}
}
на выходе получите и POST и FILE

Мой предыдущий коммент проигнорирован и удален что ли?

Все готово!

Здравствуйте!

Я на правах автора статьи, прошу убрать nofollow со ссылок на мой сайт (wp-kama.ru), как это было изначально...

Также прошу, в конце статьи указать мое авторство, а то там почему-то "источник" указан, когда на сайте у меня нет статьи посвященной это теме.

Спасибо!

П.С. не нашел контактов, поэтому пиши суда.

Если "отправлять" без файлов - ошибка :) Лечится вот такой строчкой

event.preventDefault(); // Полная остановка происходящего
if (typeof files == 'undefined') return;

Спасибо за примерчик.

Хорошая статья, но я поправил чуток, думаю народу будет тоже интереснее не пути файлов видеть а картинки. Да и в форме accept расширить accept="image/jpeg,image/png,image/gif"
подправить :
submit.php
$files[] = realpath( $uploaddir . $file['name'] );
на
$files[] = $uploaddir . $file['name'] ;

вместо:
$.each( files_path, function( key, val ){ html += val +''; } )
написать:
$.each( files_path, function( key, val ){ html += ''; } )

само собой подправить чуток css
к примеру так
.admin .ajaxRespond {
display: -webkit-flex;
display: flex;
}
.admin .ajaxRespond-imgPreview {
width: 200px;
}

А так конечно все супер, я еще накинул создание превью и ресайз короче, столкнул дело с мертвой точки. Все хотел да никак. )

Сергей

Спасибо. Всё работает, но пришлось удалить эти две строчки:
event.stopPropagation();
event.preventDefault();
иначе код дальше этих строчек не выполняется.
событие click считывается через метод on и действует на эту ссылку
Ответить
ну и данные в files считываю через метод on тоже
$('body').on('change', '#otvet-file', function () {
files = this.files;
});
иначе в file ничего не считывает.
В моём случае поле input и ссылка подгружались в модальное окно посредством ajax.
Для других случаев не проверялось может код и работает без удаления тех двух строчек.

Можно было убрать вместо двух строчек, только верхнюю, вторая останавливает стандартную обработку формы

Александр

Подскажите как уже загруженные файлы отправлять на почту?

Николай

Спасибо! Все работает, у кого не выходит просто упускают что-то, в моем случае мне был нужен только один файл и я добавил его так:
var data = new FormData();
data.append("name", $("input[type=file]")[0].files[0]);

и что бы не было ошибок обязательно использовать:
processData: false,
contentType: false,

Георгий

Смешно, ajax отправляет данные POST запросом, а на сервере почему то GET

Евгений

Присмотритесь:

url: './submit.php?uploadfiles',
type: 'POST',
data: data,

Здесь data передаётся методом POST и будет доступна через переменную $_POST, но так как это файлы, то $FILES, а uploadfiles указано GET параметром. Итого запрос идёт на ./submit.php с переменной $_GET['uploadfiles'] и переменными в $_POST / $_FILES переданные методом $_POST. Никто ж не запрещает одновременно передавать GET и POST данные.

Код из примера не работает.

Почти всё отлично работает. Два вопроса. 1. В директорию на сервере имена файлов переносятся нормально только если они в латинице. В кириллице пишется абракадабра. Причём пользователю возвращаются имена нормально. Как от этого избавиться? 2. Загружается только один последний выбранный файл. Как загрузить несколько?

Имея мозг, я им подумал), перед тем как тратить время.. А код случаем не для html5? Если так, то весь смысл теряется. Атрибуты странные в html. Без них работать будет?

Спасибо, легко и просто.

upd. даже нельзя тут свой пост отредактировать... думал о другом... ajaxupload.3.5.js конечно же

Сложно, не понятно, криво. Есть библиотека autoload.js там все проще в разы...

пиздюк!, я на твой нерабочий код пол дня потратил, рожу бы тебе набить

Фильтруй речь! код рабочий и размещен здесь для ПРИМЕРА а если ты не хочешь разбираться сам, то вали на freelance.ru и там адекватные люди сделают все за тебя

Наверно библиотеку jQuery забыл подключить

А ты не выражайся а учи JS и PHP

А то, что на сервере принимает GET, в отправляется POST'ом тебя не смутило?

Евгений

А почему должно смутить, что переменная отправленная GET параметром приходит вместе с POST запросом? Разберитесь сперва. Никто не запрещает использовать GET вместе с POST и в данном случае файлы передаются через POST а переменная uploadfiles в строке './submit.php?uploadfiles' GET параметром, если вы конечно понимаете, что переменная в адресе после вопросительного знака это всегда GET параметр.

Отличная статья. Впервые показали, пожалуй, самый простой способ загрузки файлов без перезагрузки страницы. Раньше для этих целей использовал невидимый тэг iframe , указывал его как target для формы, а потом уже из него возвращался в родительскую(текущую) форму. Приятно узнать, что это можно делать более простым способом через AJAX

Господа, в наше время за такой "пример" вызывали на дуэль.
Код не работает.

И ты бы проиграл на дуэли, код работает

Спасибо! Всё работает. Отправлял файл аяксом первый раз, Ваша статья отлично помогла разобраться. Правда не получилось отправить в формате json, пришлось использовать xml. И да, было б хорошо вынести в саму статью о data.append( key, value ); т.к. голый файл редко надо отправлять. Для each надо добавить if, если файл является необязательным и не был выбран. И раз сайт называется "о WordPress за чашкой кофе", то правильно было б привести пример именно для WP, адрес wp-admin/admin-ajax.php, и обработка ответа от WP. Это рекомендации, а так еще раз спасибо за статью.

зачем выкладывать нерабочий код?
ладно бы статья была старая, а тут изначально выложена бесполезный код...

все работает, с небольшими переделками у себя использовал

в ajax не силён, как мне второй параметр, вместе с data передать в submit.php

попробовал конструкцию
data : {'data':data, 'id':id}
не срабатывает

Вставляете так же:
data.append( key, value );
в submit.php:
$var = $_POST[key]; // получаем value

Виталий

У меня не работает
data.append(eventId, eventId);
$eventId=$_POST['eventId'];

Евгений

data.append('eventId', eventId);

Максим

Все это конечно интересно, но на HTML5 + последний jQuery не работает.
Выдает ОШИБКИ AJAX запроса: error :) до сервера дело не доходит.

Никита

Смени тип json на html и все. Правда, не во всех браузерах будет работать..