Как импортировать/загружать файлы с помощью jQuery AJAX?

1 нояб. 2012 г., 15:08:14
Просмотры: 66.2K
Голосов: 0

Я постоянно сталкиваюсь с проблемой добавления файлов через 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);
    }
}
?>
0
Все ответы на вопрос 3
2

Я не пробовал, но думаю, вам нужно добавить объект 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);
    });

Остальная часть вашего кода остается без изменений

1 нояб. 2012 г. 15:33:21
Комментарии

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

EkoJR EkoJR
1 дек. 2012 г. 11:44:29

Не говоря уже о том, что это не будет работать в IE9 и более старых версиях IE.

Arda Arda
3 нояб. 2015 г. 17:32:49
2

Ajax в традиционном понимании — это XMLHttpRequest, который не позволяет кодировать и отправлять локальные файлы на сервер.

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

Вот хороший ответ на очень похожий вопрос, где показано, как это можно реализовать.

1 нояб. 2012 г. 15:51:04
Комментарии

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

EkoJR EkoJR
2 нояб. 2012 г. 10:45:07

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

EkoJR EkoJR
1 дек. 2012 г. 11:54:53
0

Вот решение, которое мне удалось найти, и я действительно заметил несколько распространенных ошибок, которые были допущены.

  • Файлы не добавлялись должным образом в 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();
    }
}
?>
2 июл. 2017 г. 10:41:15