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


у меня проблема с отправкой файла на серверный 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);
    }
});
11 484

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){
            ...

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

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

Это привело меня к этому: (замените 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 я увидел правильный составной заголовок для этого запроса, но затем перенаправление 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.name-Вэл.значение);

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