Как импортировать/загружать файлы с помощью jQuery AJAX?
Я постоянно сталкиваюсь с проблемой добавления файлов через AJAX так, чтобы серверная ajax-функция могла обработать данные. Как мне передать $_FILES так же, как при стандартном действии (встроенном в элементы формы)?
HTML форма
<form id="frmImport" name="frmImport" method="post" enctype="multipart/form-data" >
<input id="file_import" name="importData" type="file" />
<br/>
<button id="btn_import" type="submit" >Импорт</button>
</form>
JavaScript
jQuery(document).ready(function($) {
// Это уже настроено и отправлено с серверной стороны,
// и используется для предотвращения загрузки данных неавторизованными пользователями.
var importnonce = "3x4mpl3f4k3n0nc3";
$('#frmImport').submit(function(e) {
e.preventDefault();
// ПРОВЕРКА НА НАЛИЧИЕ ОШИБОК.
// ЕСЛИ ОШИБКИ СУЩЕСТВУЮТ, ВЕРНУТЬ FALSE ДЛЯ ЗАВЕРШЕНИЯ ОПЕРАЦИЙ.
var formData = new FormData();
formData.append('action', 'ajax_handler_import');
formData.append('_ajax_nonce', importNonce);
// Проблема возникает здесь. PHP получает строку '[object FormData]'.
var importFiles = $('#file_import')[0].files;
formData.append('uploadFiles', importFiles);
jQuery.ajax({
url: ajaxurl,
type: 'POST',
cache: false,
contentType: false,
processData: false,
data: formData,
beforeSend: function(jqXHR, settings) {
console.log("Еще не вошли в серверную часть.");
},
dataFilter: function(data, type) {
// Используется для разбора данных и возможной проверки на ошибки.
console.log("JSON строка возвращена с серверной стороны.");
},
success: function(data, textStatus, jqXHR) {
console.log("Вернулись с серверной стороны!");
// Проверка ошибок, которые могли быть обнаружены на серверной стороне,
// которые обычно не отображаются в функции ошибки Ajax.
if (data.msg != 'success') {
alert(data.error);
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.log("Произошла ошибка JS.");
},
complete: function(jqXHR, textStatus) {
console.log("Ajax завершен.");
}
});
return false;
});
});
PHP Сервер/Обработчик
<?php
class server {
function ajax_import_handler() {
check_ajax_referer("ajax_handler_import");
$rtnData = new stdClass();
$rtnData->msg = 'success'; // Для обозначения серверных ошибок
$rtnData->error = ''; // Для отображения пользовательских сообщений об ошибках или использования метода try/catch
// Выполнить действия
foreach ($_FILES as $key => $value) {
// ПОЛУЧИТЬ СОДЕРЖИМОЕ ФАЙЛА
$file_array[$key] = json_decode(file_get_contents($value['tmp_name']));
}
// Выполнить дополнительные действия с файлом(ами).
// Добавить данные в $rtnData.
echo json_encode($rtnData);
}
}
?>

Я не пробовал, но думаю, вам нужно добавить объект FormData
напрямую как параметр data
. Что-то вроде этого
var ajaxData = new FormData();
ajaxData.append( 'action', 'ajax_handler_import' );
ajaxData.append( '_ajax_nonce', importNonce );
// или, возможно, пока пропустить nonce
jQuery.each($('#fileImportData')[0].files, function(i, file) {
ajaxData.append('file-'+i, file);
});
Остальная часть вашего кода остается без изменений

Извини, что не ответил раньше... Я пытался закончить проект и решил вернуться, когда у меня будет больше знаний по теме. Спасибо, что предоставил решение, я не знал, что добавление объекта FormData в другой объект может все испортить, и у меня уже заканчивались идеи. Как обычно, проблема оказалась простой xP. Урок на будущее: избегай смешанных объектов.

Ajax в традиционном понимании — это XMLHttpRequest, который не позволяет кодировать и отправлять локальные файлы на сервер.
Обычные способы загрузки файлов с использованием "ajax" включают либо использование Flash swf для обработки загрузки на той же странице, либо использование формы с target в виде невидимого iframe размером 1x1.
Вот хороший ответ на очень похожий вопрос, где показано, как это можно реализовать.

Да, я недавно просматривал этот вопрос/ответ. К сожалению, я не знаком с Flash и по возможности стараюсь избегать добавления плагинов. В основном я пытался следовать методу WordPress Ajax, но, как я выяснил, просто передача через ajax ограничена/лимитирована браузерами. В основном ссылаясь на этот Q/A, я наткнулся на FormData Objects, где сказано, что это возможно, если браузер поддерживает эту функцию.

Оказывается, можно использовать собственные методы передачи файловых данных в AJAX-функцию, не полагаясь на JS-плагин. В большинстве случаев FormData Objects (как упоминалось выше) обрабатывает это с помощью операций XMLHttpRequest Level 2 (насколько я сейчас понимаю) и поддерживается всеми основными браузерами. Я предполагал, что это вопрос времени после выхода HTML5. Тем не менее, использование plUpload можно порекомендовать для передачи больших файлов.

Вот решение, которое мне удалось найти, и я действительно заметил несколько распространенных ошибок, которые были допущены.
- Файлы не добавлялись должным образом в JS.
- AJAX
dataFilter: function()
можно заменить наdataType: 'json'
. - В JS submit не нужно возвращать false, чтобы остановить действие формы.
- Можно заменить на
event.stopPropagation()
иe.preventDefault()
. - Добавить пустое действие формы в HTML.
- Можно заменить на
- PHP-функция для AJAX не завершалась die().
HTML
<form id="form_import" name="form_import" method="post" enctype="multipart/form-data" action >
<input id="file_import" name="import_data" type="file" />
<br/>
<button id="btn_import" type="submit" >Импорт</button>
</form>
JavaScript
jQuery(document).ready(function($){
// PHP-файлы могут выводить Nonce. Однако это локализует скрипты.
var importNonce = localizedData.import_nonce;
$('#form_import').submit( function( event ) {
event.stopPropagation(); // Остановить выполнение событий
event.preventDefault(); // Полностью остановить выполнение событий
var formData = new FormData();
var importFiles = $('#file_import')[0].files;
// Для КАЖДОГО файла добавить в formData.
// ПРИМЕЧАНИЕ: Простое добавление всех importFiles не очень хорошо работает с PHP.
jQuery.each( importFiles, function( index, value ) {
var name = 'file_' + index;
formData.append( name, value )
});
formData.append( 'action', 'ajax_file_import' );
formData.append( '_ajax_nonce', importNonce );
jQuery.ajax({
url: ajaxurl,
type: 'POST',
data: formData,
cache: false,
dataType: 'json', // Это заменяет dataFilter: function() && JSON.parse( data ).
processData: false, // Не обрабатывать файлы
contentType: false, // Установить content type в false, так как jQuery сообщит серверу, что это запрос строки запроса
beforeSend: function( jqXHR, settings ){
// (ОПЦИОНАЛЬНО) Альтернативная концепция AJAX.
// Удаляет старый IFrame, если он есть.
var element = document.getElementById('import_IF');
if ( element !== null ){
element.parentNode.removeChild( element );
}
// КОНЕЦ (ОПЦИОНАЛЬНО).
},
success: function( data, textStatus, jqXHR ) {
console.log( 'Возврат из PHP AJAX функции/метода.' );
// Делаем что-то с data.values
// (ОПЦИОНАЛЬНО) Альтернативная концепция AJAX.
// Необходимо для загрузки в AJAX.
var paramStr = '';
paramStr += '?_ajax_nonce=' + data._ajax_nonce;
paramStr += '&action=' + data.action;
paramStr += '&filename=' + data.filename;
var elemIF = document.createElement("iframe");
elemIF.id = 'import_IF'
elemIF.style.display = "none";
elemIF.src = ajaxurl + paramStr;
document.body.appendChild(elemIF);
elemIF.parentNode.removeChild(elemIF);
// КОНЕЦ (ОПЦИОНАЛЬНО).
},
complete: function(jqXHR, textStatus){
console.log( 'AJAX завершен.' );
// (ОПЦИОНАЛЬНО) Альтернативная концепция AJAX.
// Очистка IFrame.
var element = document.getElementById('import_IF');
if ( element !== null ){
element.parentNode.removeChild( element );
}
// КОНЕЦ (ОПЦИОНАЛЬНО).
}
});// Конец AJAX.
});// Конец .submit().
});
PHP
<?php
class server {
public function __construct() {
// Пример добавления скрипта.
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_example' ) );
add_action( 'wp_ajax_ajax_file_import', array( $this, 'ajax_import_example' ) );
}
public funtion enqueue_example() {
wp_register_script(
'example-js',
FILE_URL,
array(),
VERSION,
false
);
wp_enqueue_script( 'example-js' );
$data = array(
'import_nonce' => wp_create_nonce( 'ajax_file_import_nonce' ),
);
wp_localize_script( 'example-js', 'localizedData', $data );
}
public function ajax_import_example() {
check_ajax_referer( 'ajax_file_import_nonce' );
$raw_content = array();
$i = 0;
while ( isset( $_FILES[ 'file_' . $i ] ) ) {
$file_arr = $_FILES[ 'file_' . $i ];
$file_content = file_get_contents( $file_arr['tmp_name'] );
$raw_content[] = json_decode( $file_content );
$i++;
}
$new_data = array();
// Делаем что-то с новыми данными.
update_option( 'example_database', $new_data );
// (ОПЦИОНАЛЬНО) Добавляем данные для возврата в AJAX Success
$rtn_data = array(
'action' => 'apl_import',
'_ajax_nonce' => wp_create_nonce( 'alt_ajax' ),
);
echo json_encode( $rtn_data );
// КОНЕЦ (ОПЦИОНАЛЬНО).
die();
}
}
?>
