Отправка составные/виде FormData с jQuery.Аякс

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

Я знаю, что это возможно (хотя до сих пор я не нашел никаких решений 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);
    }
});

11 ответов


начиная с 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);
    }
});

необходимо, чтобы вы установили до 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'] затем будет массив, содержащий поля загрузки файлов для каждого загруженного файла. Я действительно рекомендую это над моим начальное решение, поскольку его проще перебирать.


просто хотел добавить немного к замечательному ответу Рафаэля. Вот как заставить 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
                )

        )

)

если вы делаете прогрессивное улучшение, используя 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
        )

)

это хороший массив, и на самом деле то, что некоторые люди превращают $_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 edit: я удалил элемент формы из конструктора FormData ()--, который исправил этот код в Safari.)

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

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

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


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

использовать его как с помощью .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);

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

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

Если ваша форма определена в вашем 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){
            ...

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

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

Это привело меня к этому: (замените 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.
    });

});

класс 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"));

и в Рафаэля код.


один gotcha, с которым я столкнулся сегодня, я думаю, стоит указать на эту проблему: если url-адрес для вызова ajax перенаправлен, то заголовок для content-type: "multipart/form-data" может быть потерян.

, Я отправлял в http://server.com/context?param=x

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

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


более старые версии 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


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

formData.append (val.имя, Вэл.value);


  1. получить объект формы с помощью jquery - > $("#id") [0]
  2. data = new FormData ($("#id") [0]);
  3. ok, данные-это ваше желание