it-roy-ru.com

Отправка multipart/formdata с помощью jQuery.ajax

У меня проблема с отправкой файла на серверный PHP-скрипт с использованием ajax-функции jQuery .... Возможно получить список файлов с помощью $('#fileinput').attr('files'), но как можно отправить эти данные на сервер? Результирующий массив ($_POST) на php-скрипте на стороне сервера равен 0 (NULL) при использовании файлового ввода.

Я знаю, что это возможно (хотя я до сих пор не нашел решений jQuery, только код Prototye ( http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress. HTML )).

Это кажется относительно новым, поэтому, пожалуйста, не упоминайте, что загрузка файлов будет невозможна через XHR/Ajax, потому что это определенно работает.

Мне нужна функциональность в Safari 5, FF и Chrome были бы хорошими, но не обязательными.

Мой код сейчас:

$.ajax({
    url: 'php/upload.php',
    data: $('#file').attr('files'),
    cache: false,
    contentType: 'multipart/form-data',
    processData: false,
    type: 'POST',
    success: function(data){
        alert(data);
    }
});
504
zoku

Начиная с Safari 5/Firefox 4, проще всего использовать класс FormData:

var data = new FormData();
jQuery.each(jQuery('#file')[0].files, function(i, file) {
    data.append('file-'+i, file);
});

Итак, теперь у вас есть объект FormData, готовый к отправке вместе с XMLHttpRequest.

jQuery.ajax({
    url: 'php/upload.php',
    data: data,
    cache: false,
    contentType: false,
    processData: false,
    method: 'POST',
    type: 'POST', // For jQuery < 1.9
    success: function(data){
        alert(data);
    }
});

Крайне важно, чтобы вы установили для параметра contentType значение false, заставляя jQuery не добавлять для вас заголовок Content-Type, в противном случае строка границы будет отсутствовать в нем . Кроме того, вы должны оставить для флага processData значение false, иначе jQuery попытается преобразовать ваш FormData в строку, что не удастся.

Теперь вы можете получить файл в PHP, используя:

$_FILES['file-0']

(Существует только один файл, file-0, если только вы не указали атрибут multiple во входном файле, и в этом случае числа будут увеличиваться с каждым файлом.)

Использование эмуляции FormData для старых браузеров

var opts = {
    url: 'php/upload.php',
    data: data,
    cache: false,
    contentType: false,
    processData: false,
    method: 'POST',
    type: 'POST', // For jQuery < 1.9
    success: function(data){
        alert(data);
    }
};
if(data.fake) {
    // Make sure no text encoding stuff is done by xhr
    opts.xhr = function() { var xhr = jQuery.ajaxSettings.xhr(); xhr.send = xhr.sendAsBinary; return xhr; }
    opts.contentType = "multipart/form-data; boundary="+data.boundary;
    opts.data = data.toString();
}
jQuery.ajax(opts);

Создать FormData из существующей формы

Вместо ручной итерации файлов, объект FormData также может быть создан с содержимым существующего объекта формы:

var data = new FormData(jQuery('form')[0]);

Использовать собственный PHP массив вместо счетчика

Просто назовите элементы вашего файла одинаково и оканчивайте имя в скобках:

jQuery.each(jQuery('#file')[0].files, function(i, file) {
    data.append('file[]', file);
});

$_FILES['file'] будет тогда массивом, содержащим поля загрузки файла для каждого загруженного файла. Я на самом деле рекомендую это поверх моего первоначального решения, так как его проще повторить.

814
Raphael Schweikert

Просто хотел добавить немного к отличному ответу Рафаэля. Вот как заставить PHP создавать тот же $_FILES, независимо от того, используете ли вы JavaScript для отправки.

HTML-форма:

<form enctype="multipart/form-data" action="/test.php" 
method="post" class="putImages">
   <input name="media[]" type="file" multiple/>
   <input class="button" type="submit" alt="Upload" value="Upload" />
</form>

PHP создает этот $_FILES, когда отправлено без JavaScript:

Array
(
    [media] => Array
        (
            [name] => Array
                (
                    [0] => Galata_Tower.jpg
                    [1] => 518f.jpg
                )

            [type] => Array
                (
                    [0] => image/jpeg
                    [1] => image/jpeg
                )

            [tmp_name] => Array
                (
                    [0] => /tmp/phpIQaOYo
                    [1] => /tmp/phpJQaOYo
                )

            [error] => Array
                (
                    [0] => 0
                    [1] => 0
                )

            [size] => Array
                (
                    [0] => 258004
                    [1] => 127884
                )

        )

)

Если вы делаете прогрессивное улучшение, используя Raphael JS для отправки файлов ...

var data = new FormData($('input[name^="media"]'));     
jQuery.each($('input[name^="media"]')[0].files, function(i, file) {
    data.append(i, file);
});

$.ajax({
    type: ppiFormMethod,
    data: data,
    url: ppiFormActionURL,
    cache: false,
    contentType: false,
    processData: false,
    success: function(data){
        alert(data);
    }
});

... вот как выглядит массив PHP $_FILES после использования этого JavaScript для отправки:

Array
(
    [0] => Array
        (
            [name] => Galata_Tower.jpg
            [type] => image/jpeg
            [tmp_name] => /tmp/phpAQaOYo
            [error] => 0
            [size] => 258004
        )

    [1] => Array
        (
            [name] => 518f.jpg
            [type] => image/jpeg
            [tmp_name] => /tmp/phpBQaOYo
            [error] => 0
            [size] => 127884
        )

)

Это массив Nice, и на самом деле то, во что некоторые люди преобразуют $_FILES, но я считаю, что полезно работать с тем же $_FILES, независимо от того, использовался ли JavaScript для отправки. Итак, вот некоторые незначительные изменения в JS:

// match anything not a [ or ]
regexp = /^[^[\]]+/;
var fileInput = $('.putImages input[type="file"]');
var fileInputName = regexp.exec( fileInput.attr('name') );

// make files available
var data = new FormData();
jQuery.each($(fileInput)[0].files, function(i, file) {
    data.append(fileInputName+'['+i+']', file);
});

(14 апреля 2017 г., редактирование: я удалил элемент формы из конструктора FormData () - это исправило этот код в Safari.)

Этот код делает две вещи.

  1. Извлекает атрибут имени input автоматически, делая HTML более понятным. Теперь, пока form имеет класс putImages, все остальное решается автоматически. То есть input не должен иметь никакого специального имени.
  2. Формат массива, который отправляет обычный HTML, воссоздается JavaScript в строке data.append. Обратите внимание на скобки.

С этими изменениями отправка с помощью JavaScript теперь создает точно такой же массив $_FILES, что и отправка с простым HTML.

46
ajmicek

Посмотри на мой код, он делает всю работу за меня

$( '#formId' )
  .submit( function( e ) {
    $.ajax( {
      url: 'FormSubmitUrl',
      type: 'POST',
      data: new FormData( this ),
      processData: false,
      contentType: false
    } );
    e.preventDefault();
  } );
44
Asad Malik

Я только построил эту функцию, основываясь на некоторой информации, которую я прочитал.

Используйте это как использование .serialize(), вместо этого просто поставьте .serializefiles();.
Работаю здесь в моих тестах.

//USAGE: $("#form").serializefiles();
(function($) {
$.fn.serializefiles = function() {
    var obj = $(this);
    /* ADD FILE TO PARAM AJAX */
    var formData = new FormData();
    $.each($(obj).find("input[type='file']"), function(i, tag) {
        $.each($(tag)[0].files, function(i, file) {
            formData.append(tag.name, file);
        });
    });
    var params = $(obj).serializeArray();
    $.each(params, function (i, val) {
        formData.append(val.name, val.value);
    });
    return formData;
};
})(jQuery);
44
evandro777

Если ваша форма определена в вашем HTML, проще передать форму в конструктор, чем итерировать и добавлять изображения.

$('#my-form').submit( function(e) {
    e.preventDefault();

    var data = new FormData(this); // <-- 'this' is your form element

    $.ajax({
            url: '/my_URL/',
            data: data,
            cache: false,
            contentType: false,
            processData: false,
            type: 'POST',     
            success: function(data){
            ...
23
Devin Venable

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

Я также хотел использовать метод on () jQuery, чтобы избежать использования .ready ().

Это привело меня к следующему: (Замените formSelector на ваш селектор jQuery)

$(document).on('submit', formSelecter, function( e ) {
        e.preventDefault();
    $.ajax( {
        url: $(this).attr('action'),
        type: 'POST',
        data: new FormData( this ),
        processData: false,
        contentType: false
    }).done(function( data ) {
        //do stuff with the data you got back.
    });

});
6
Karl Henselin

Класс FormData работает, однако в iOS Safari (по крайней мере, на iPhone) я не смог использовать решение Рафаэля Швейкера как есть.

У Mozilla Dev есть хорошая страница по манипулированию объектами FormData .

Итак, добавьте пустую форму где-нибудь на вашей странице, указав enctype:

<form enctype="multipart/form-data" method="post" name="fileinfo" id="fileinfo"></form>

Затем создайте объект FormData как:

var data = new FormData($("#fileinfo"));

и действуйте как в код Рафаэля .

1
topkara

Более старые версии IE не поддерживают FormData (полный список поддержки браузера для FormData находится здесь: https://developer.mozilla.org/en-US/docs/Web/API/FormData ).

Либо вы можете использовать плагин jquery (например, http://malsup.com/jquery/form/#code-samples ), либо вы можете использовать решение на основе IFrame для публикации данных из нескольких форм через ajax: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript

0
sudip

В наши дни вам даже не нужен jQuery :) получить таблицу поддержки API

let result = fetch('url', {method: 'POST', body: new FormData(documemt.querySelector("#form"))})
0
Alex Nikulin

Одна проблема, с которой я столкнулся сегодня, я думаю, стоит упомянуть, связанную с этой проблемой: если URL для вызова ajax перенаправлен, тогда заголовок для типа содержимого: 'multipart/form-data' может быть потерян.

Например, я писал в http://server.com/context?param=x

На вкладке сети Chrome я увидел правильный многокомпонентный заголовок для этого запроса, но затем перенаправил 302 на http://server.com/context/?param=x (обратите внимание на косую черту после контекста)

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

0
james

Все решения, представленные выше, выглядят хорошо и элегантно, но объект FormData () не ожидает какого-либо параметра, но использует метод append () после его создания, например, как написано выше:

formData.append (val.name, val.value);

0
szatti1489