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-поле, з’являється вікно вибору файлів, після вибору, дані про них зберігаються в полі, а нам потрібно їх від туди «забрати». Для цього повісимо на подію change JS функцію, яка зберігатиме наявні дані file-поля в JS змінну files :

var files; // Змінна. міститиме дані файлів

// Заповнюємо змінну даними, при зміні значення поля file
$('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 multipart/form-data»… Загалом, найпростіше відключити цю опцію, тоді все працює!
// обробка та відправка AJAX запиту при натисканні на кнопку upload_files
$('.upload_files').on( 'click', function( event ){

	event.stopPropagation(); // зупинка всіх поточних JS подій
	event.preventDefault(); // зупинка дефолтної події для поточного елемента - клік для тега

	// нічого не робимо якщо files порожній
	if( typeof files == 'undefined' ) return;

	// Створимо об'єкт даних форми
	var data = новий 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

	// Створимо папку якщо її немає
	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

Повний, готовий до роботи код цієї статті.
Завантажено: 5060, розмір: 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(); // зупинка дефолтної події для поточного елемента - клік для тега

				// нічого не робимо якщо files порожній
				if( typeof files == 'undefined' ) return;

				// Створимо дані файлів у відповідному для відправки форматі
				var data = новий 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 );

}

Залишити коментар

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *