WordPress как на ладони
Очень Удобный и Быстрый Хостинг для сайтов на WordPress. Пользуюсь сам и вам рекомендую!

jQuery AJAX загрузка файлов на сервер

Как загружать любые файлы, например, картинки на сервер с помощью AJAX и jQuery? Делается это довольно просто! И ниже мы все обстоятельно разберем.

В те «древние» времена, когда еще не было jQuery, а может он был, но браузеры были не так наворочены, загрузка файла на сайт с помощью AJAX была делом муторным: через всякие костыли вроде iframe. Я то время не застал, да и кому это теперь интересно. А интересно теперь другое - что сохранение файлов на сайт делается очень просто. Даже не обладающий опытом и пониманием, того как работает AJAX, вебмастер, сможет быстро разобраться что-куда. А эта статья ему в помощь. Если подкрепить эти возможности функциями WordPress, то безопасная обработка и загрузка файлов на сервер становится совсем плёвым и даже интересным делом (пример с WordPress смотрите в конце статьи).

Однако, как бы все просто не было, нужно заметить, что минимальный опыт работы с файлами и базовые знания в Javascript, jQuery и PHP все же необходимы! Минимум, нужно представлять как загружаются файлы на сервер, как в общих чертах работает AJAX и хоть немного надо уметь читать и понимать код.

Описанный ниже метод довольно стабилен, и по сути опирается на Javascript объект new FormData(), базовая поддержка которого есть во всех браузерах.

Для более понятного восприятия материала, он разделен на шаги. На этом все, полетели...

AJAX Загрузка файлов: общий пример

Начинается все с наличия на сайте input поля типа file. Нет необходимости, чтобы это поле было частью формы (тега <form>).

Таким образом, у нас есть HTML код с file полем и кнопкой «Загрузить файлы».

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

Шаг 1. Данные из поля file

Первым шагом, нужно получить данные загружаемых файлов.

При клике на file-поле, появляется окно выбора файлов, после выбора, данные о них сохраняются в input поле, а нам нужно их от туда «забрать». Для этого повесим на событие change JS функцию, которая будет сохранять имеющиеся данные file-поля в JS переменную files:

var files; // переменная. будет содержать данные файлов

// заполняем переменную данными, при изменении значения поля file
jQuery('input[type=file]').on('change', function(){
	files = this.files;
});

Шаг 2. Создаем AJAX запрос (по клику)

Данные файлов у нас есть, теперь их нужно отправить через AJAX. Вешаем это событие на клик по кнопке «Загрузить файлы».

В момент клика создаем новый объект new formData() и добавляем в него данные из переменной files. С помощью formData() мы добьемся того, что отправляемые данные будут выглядеть, как если бы мы просто сабмитили форму в браузере.

Далее, из имеющихся данных формы создаем нестандартный AJAX запрос, в котором передаем файлы в стандартном для сервера формате: $_FILES.

Чтобы такой запрос состоялся, в jQuery нужно указать дополнительные AJAX параметры, поэтому привычная функция $.post() не подходит и мы используем более гибкий аналог: $.ajax().

Два важных дополнительных параметра нужно установить в false:

processData
Отключает обработку передаваемых данных. По умолчанию, например, для GET запросов jQuery собирает данные в строку запроса и добавляет эту строку в конец URL. Для POST данных делает другие преобразования. Нам любые изменения исходных данных будут мешать, поэтому отключаем эту опцию...
contentType
Отключает установку заголовка типа запроса. Дефолтная установка jQuery равна "application/x-www-form-urlencoded". Такой заголовок не предусматривает отправку файлов. Если установить этот параметр в "multipart/form-data", PHP все равно не сможет распознать передаваемые данные и выведет предупреждение «Missing boundary in multipart/form-data»... В общем, проще всего отключить эту опция, тогда все работает!
// обработка и отправка AJAX запроса при клике на кнопку upload_files
jQuery('.upload_files').on( 'click', function( event ){

	event.stopPropagation(); // остановка всех текущих JS событий
	event.preventDefault();  // остановка дефолтного события для текущего элемента - клик для <a> тега

	// ничего не делаем если files пустой
	if( typeof files == 'undefined' ) return;

	// создадим объект данных формы
	var data = new FormData();

	// заполняем объект данных файлами в подходящем для отправки формате
	$.each( files, function( key, value ){
		data.append( key, value );
	});

	// добавим переменную для идентификации запроса
	data.append( 'my_file_upload', 1 );

	// AJAX запрос
	$.ajax({
		url         : './submit.php',
		type        : 'POST', // важно!
		data        : data,
		cache       : false,
		dataType    : 'json',
		// отключаем обработку передаваемых данных, пусть передаются как есть
		processData : false,
		// отключаем установку заголовка типа запроса. Так jQuery скажет серверу что это строковой запрос
		contentType : false,
		// функция успешного ответа сервера
		success     : function( respond, status, jqXHR ){

			// ОК - файлы загружены
			if( typeof respond.error === 'undefined' ){
				// выведем пути загруженных файлов в блок '.ajax-reply'
				var files_path = respond.files;
				var html = '';
				$.each( files_path, function( key, val ){
					 html += val +'<br>';
				} )

				$('.ajax-reply').html( html );
			}
			// ошибка
			else {
				console.log('ОШИБКА: ' + respond.data );
			}
		},
		// функция ошибки ответа сервера
		error: function( jqXHR, status, errorThrown ){
			console.log( 'ОШИБКА AJAX запроса: ' + status, jqXHR );
		}

	});

});

Шаг 3. Обрабатываем запрос: загружаем файлы на сервер

Теперь последний шаг: нужно обработать отправленный запрос.

Чтобы было наглядно обработаем запрос без дополнительных проверок для файлов, т.е. просто сохраним полученные файлы в нужную папку. Хотя, для безопасности, отправляемые файлы обязательно нужно проверять, хотя бы расширение (тип) файла...

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

<?php

if( isset( $_POST['my_file_upload'] ) ){
	// ВАЖНО! тут должны быть все проверки безопасности передавемых файлов и вывести ошибки если нужно

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

	// cоздадим папку если её нет
	if( ! is_dir( $uploaddir ) ) mkdir( $uploaddir, 0777 );

	$files      = $_FILES; // полученные файлы
	$done_files = array();

	// переместим файлы из временной директории в указанную
	foreach( $files as $file ){
		$file_name = $file['name'];

		if( move_uploaded_file( $file['tmp_name'], "$uploaddir/$file_name" ) ){
			$done_files[] = realpath( "$uploaddir/$file_name" );
		}
	}

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

	die( json_encode( $data ) );
}

Вот и все!

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

-

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

ajax-file-upload.zip
Полный, готовый к работе код из этой статьи.
Скачано: 5686, размер: 3.2 KB

Скопируйте содержимое архива на ваш php сервер, зайдите в главную паку (в браузере) и попробуйте загрузить файлы. Так, вы «в живую» увидите что и как работает.

AJAX Загрузка файлов: пример для WordPress

Для WordPress обрабатывать AJAX запрос в разы проще, потому что есть готовые функции, например media_handle_upload().

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

Чтобы код ниже начал работать, его нужно добавить в файл темы functions.php. Далее, создать страницу с ярлыком ajax_file_upload и зайти на эту страницу. В контенте вы увидите форму для добавления файла. Выбираете файлы и проверяете все ли загрузилось...

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

<?php

// форма
add_action( 'the_content', 'ajax_file_upload_html' );

// скрипт
add_action( 'wp_footer', 'ajax_file_upload_jscode' );

// AJAX обработчик
add_action( 'wp_ajax_'.'ajax_fileload',        'ajax_file_upload_callback' );
add_action( 'wp_ajax_nopriv_'.'ajax_fileload', 'ajax_file_upload_callback' );

// HTML код формы
function ajax_file_upload_html( $text ){
	// выходим не наша страница...
	if( $GLOBALS['post']->post_name !== 'ajax_file_upload' )
		return $text;

	return $text .= '
		<input type="file" multiple="multiple" accept="image/*">
		<button class="upload_files">Загрузить файл</button>
		<div class="ajax-reply"></div>
	';
}

// JS код
function ajax_file_upload_jscode(){
	?>
	<script>
		jQuery(document).ready(function($){

			// ссылка на файл AJAX  обработчик
			var ajaxurl = '<?= admin_url('admin-ajax.php') ?>';
			var nonce   = '<?= wp_create_nonce('uplfile') ?>';

			var files; // переменная. будет содержать данные файлов

			// заполняем переменную данными, при изменении значения поля file
			$('input[type=file]').on('change', function(){
				files = this.files;
			});

			// обработка и отправка AJAX запроса при клике на кнопку upload_files
			$('.upload_files').on( 'click', function( event ){

				event.stopPropagation(); // остановка всех текущих JS событий
				event.preventDefault();  // остановка дефолтного события для текущего элемента - клик для <a> тега

				// ничего не делаем если files пустой
				if( typeof files == 'undefined' ) return;

				// создадим данные файлов в подходящем для отправки формате
				var data = new FormData();
				$.each( files, function( key, value ){
					data.append( key, value );
				});

				// добавим переменную идентификатор запроса
				data.append( 'action', 'ajax_fileload' );
				data.append( 'nonce', nonce );
				let pid_match = $(document.body).attr('class').match( /postid-([0-9]+)/ );
				data.append( 'post_id', pid_match ? pid_match[1] : 0 );

				var $reply = $('.ajax-reply');

				// AJAX запрос
				$reply.text( 'Загружаю...' );
				$.ajax({
					url         : ajaxurl,
					type        : 'POST',
					data        : data,
					cache       : false,
					dataType    : 'json',
					// отключаем обработку передаваемых данных, пусть передаются как есть
					processData : false,
					// отключаем установку заголовка типа запроса. Так jQuery скажет серверу что это строковой запрос
					contentType : false,
					// функция успешного ответа сервера
					success     : function( respond, status, jqXHR ){
						// ОК
						if( respond.success ){
							$.each( respond.data, function( key, val ){
								$reply.append( '<p>'+ val +'</p>' );
							} );
						}
						// error
						else {
							$reply.text( 'ОШИБКА: ' + respond.error );
						}
					},
					// функция ошибки ответа сервера
					error: function( jqXHR, status, errorThrown ){
						$reply.text( 'ОШИБКА AJAX запроса: ' + status );
					}

				});

			});

		})
	</script>
	<?php
}

// обработчик AJAX запроса
function ajax_file_upload_callback(){
	check_ajax_referer( 'uplfile', 'nonce' ); // защита

	if( empty( $_FILES ) )
		wp_send_json_error( 'Файлов нет...' );

	$post_id = (int) $_POST['post_id'];

	// ограничим размер загружаемой картинки
	$sizedata = getimagesize( $_FILES['upfile']['tmp_name'] );
	$max_size = 2000;
	if( $sizedata[0]/*width*/ > $max_size || $sizedata[1]/*height*/ > $max_size )
		wp_send_json_error( __('Картинка не может быть больше чем '. $max_size .'px в ширину или высоту...','km') );

	// обрабатываем загрузку файла
	require_once ABSPATH . 'wp-admin/includes/image.php';
	require_once ABSPATH . 'wp-admin/includes/file.php';
	require_once ABSPATH . 'wp-admin/includes/media.php';

	// фильтр допустимых типов файлов - разрешим только картинки
	add_filter( 'upload_mimes', function( $mimes ){
		return [
			'jpg|jpeg|jpe' => 'image/jpeg',
			'gif'          => 'image/gif',
			'png'          => 'image/png',
		];
	} );

	$uploaded_imgs = array();

	foreach( $_FILES as $file_id => $data ){
		$attach_id = media_handle_upload( $file_id, $post_id );

		// ошибка
		if( is_wp_error( $attach_id ) )
			$uploaded_imgs[] = 'Ошибка загрузки файла `'. $data['name'] .'`: '. $attach_id->get_error_message();
		else
			$uploaded_imgs[] = wp_get_attachment_url( $attach_id );
	}

	wp_send_json_success( $uploaded_imgs );

}
16 комментариев
Полезные 1Вопросы 2 Все
    Войти