Wysyłanie multipart / formdata za pomocą jQuery.ajax

Mam problem z wysłaniem pliku na serwer skrypt PHP przy użyciu funkcji ajax jQuery. Możliwe jest uzyskanie listy plików za pomocą $('#fileinput').attr('files'), ale jak można wysłać te dane na serwer? Tablica wynikowa ($_POST) na serwerze skryptu php wynosi 0 (NULL) Podczas używania pliku-input.

Wiem, że to możliwe (choć do tej pory nie znalazłem żadnych rozwiązań jQuery, tylko Kod Prototye (http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html)).

To wydaje się być stosunkowo nowe, więc proszę nie wspominać przesyłanie plików byłoby niemożliwe przez XHR / Ajax, ponieważ to na pewno działa.

[4]} potrzebuję funkcjonalności w Safari 5, FF i Chrome byłoby miło, ale nie są niezbędne.

Mój kod na razie to:

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

11 answers

Począwszy od Safari 5/Firefox 4, najłatwiej jest użyć klasy FormData:

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

Więc teraz masz FormData obiekt gotowy do wysłania wraz z 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);
    }
});

Konieczne jest, aby ustawić opcję contentType na false, zmuszając jQuery do nie dodawania nagłówka Content-Type dla Ciebie, w przeciwnym razie nie będzie w nim ciągu granicznego. Należy również pozostawić flagę processData ustawioną na false, w przeciwnym razie jQuery spróbuje przekonwertować FormData na łańcuch znaków, który porażka.

Możesz teraz pobrać plik w PHP używając:

$_FILES['file-0']

(istnieje tylko jeden plik, file-0, chyba że podałeś atrybut multiple na wejściu pliku, w którym to przypadku liczby będą wzrastać z każdym plikiem.)

Korzystanie z emulacji FormData dla starszych przeglądarek

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);

Tworzenie FormData z istniejącego formularza

Zamiast ręcznie iterować pliki, obiekt FormData można również utworzyć za pomocą zawartość istniejącego obiektu formularza:

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

Użycie natywnej tablicy PHP zamiast licznika

Po prostu nazwij elementy pliku tak samo i zakończ nazwę w nawiasach:

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

$_FILES['file'] będzie to tablica zawierająca pola przesyłania plików dla każdego przesłanego pliku. Faktycznie polecam to w stosunku do mojego początkowego rozwiązania, ponieważ jest prostsze do iteracji.

 782
Author: Raphael Schweikert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-05 20:13:40

Chciałem tylko dodać trochę do wspaniałej odpowiedzi Rafaela. Oto jak sprawić, by PHP stworzyło ten sam $_FILES, niezależnie od tego, czy używasz JavaScript do przesyłania.

Formularz 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 tworzy to $_FILES, gdy jest złożone bez 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
                )

        )

)

Jeśli zrobisz progresywne ulepszenie, użyj js Rafaela do przesłania plików...

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);
    }
});

... tak wygląda tablica PHP $_FILES po użyciu JavaScript do przesłania:

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
        )

)

To Ładna tablica, a właściwie to w co niektórzy przekształcają $_FILES, ale uważam, że warto pracować z tym samym $_FILES, niezależnie od tego, czy JavaScript został użyty do przesłania. Tak więc, oto kilka drobnych zmian w 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 kwietnia 2017 edit: usunąłem element formularza z konstruktora FormData() -- który naprawił ten kod w Safari.)

Ten kod robi dwie rzeczy.

  1. pobiera atrybut input name automatycznie, dzięki czemu HTML jest łatwiejszy do utrzymania. Teraz, jako o ile form ma klasę putImages, Wszystko inne jest obsługiwane automatycznie. Oznacza to, że input nie muszą mieć żadnej specjalnej nazwy.
  2. format tablicy, który przesyła zwykły HTML, jest odtwarzany przez JavaScript w danych.Dołącz linię. Zwróć uwagę na nawiasy.

Z tymi zmianami, przesyłanie za pomocą JavaScript tworzy teraz dokładnie taką samą tablicę $_FILES Jak przesyłanie za pomocą prostego HTML.

 46
Author: ajmicek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-14 17:00:50

Właśnie zbudowałem tę funkcję na podstawie informacji, które przeczytałem.

Użyj go jak używając .serialize(), zamiast tego po prostu umieść .serializefiles();.
Pracuję tu przy testach.

//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);
 43
Author: evandro777,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-10 17:01:07

Spójrz na mój kod, robi to za mnie

$( '#formId' )
  .submit( function( e ) {
    $.ajax( {
      url: 'FormSubmitUrl',
      type: 'POST',
      data: new FormData( this ),
      processData: false,
      contentType: false
    } );
    e.preventDefault();
  } );
 39
Author: Asad Malik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-04 05:06:22

Jeśli formularz jest zdefiniowany w HTML, łatwiej jest przekazać formularz do konstruktora niż iterację i dodawanie obrazów.

$('#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){
            ...
 22
Author: Devin Venable,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-02 17:43:39

Odpowiedź Devina Venable ' a była zbliżona do tego, czego chciałem, ale chciałem takiego, który będzie działał na wielu formularzach i wykorzysta akcję określoną już w formularzu, aby każdy plik trafił we właściwe miejsce.

Chciałem również użyć metody jQuery ' s on (), aby uniknąć używania .ready ().

That got me to this: (zastąp formSelector selektorem 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.
    });

});
 5
Author: Karl Henselin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45

Klasa FormData działa, jednak w iOS Safari (przynajmniej na iPhonie) nie byłem w stanie użyć rozwiązania Raphaela Schweikerta tak jak jest.

Mozilla dev ma ładną Stronę na temat manipulowania obiektami FormData .

Więc dodaj pusty formularz gdzieś na swojej stronie, podając enctype:

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

Następnie utwórz obiekt FormData jako:

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

I postępować jak w Kod Rafaela .

 1
Author: topkara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45

Jedna wpadka, na którą dziś wpadłem, myślę, że warto zwrócić uwagę na ten problem: jeśli adres url wywołania ajax zostanie przekierowany, to nagłówek dla content-type: 'multipart/form-data' może zostać utracony.

Na przykład pisałem do http://server.com/context?param=x

W zakładce sieć Chrome widziałem poprawny nagłówek multipart dla tego żądania, ale potem przekierowanie 302 do http://server.com/context/?param=x (zwróć uwagę na ukośnik po context)

Podczas przekierowania utracono nagłówek wieloczęściowy. Upewnij się, że żądania nie są przekierowywane, jeśli te rozwiązania nie działają dla Ciebie.

 0
Author: james,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-17 22:07:16

Starsze wersje IE nie obsługują FormData ( pełna lista wsparcia przeglądarki dla FormData znajduje się tutaj: https://developer.mozilla.org/en-US/docs/Web/API/FormData).

Albo możesz użyć wtyczki jquery (dla ex, http://malsup.com/jquery/form/#code-samples ) lub, można użyć rozwiązania IFrame oparte na danych formularza wieloczęściowego poprzez ajax: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript

 0
Author: sudip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-08 10:42:19

Wszystkie powyższe rozwiązania wyglądają dobrze i elegancko, ale obiekt FormData () nie oczekuje żadnego parametru, ale używa append () po utworzeniu instancji, tak jak napisano powyżej:

FormData. append(val.name, val.wartość);

 0
Author: szatti1489,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-08 13:47:10
  1. get form object by jquery - > $("#id") [0]
  2. data = new FormData ($("#id")[0]);
  3. Ok,data jest twoim pragnieniem
 -1
Author: user1909226,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-18 10:11:39