Senden von mehrteiligen / Formulardaten mit jQuery.ajax


563

Ich habe ein Problem beim Senden einer Datei an ein serverseitiges PHP-Skript mit der Ajax-Funktion von jQuery. Es ist möglich, die Dateiliste mit zu erhalten, $('#fileinput').attr('files')aber wie ist es möglich, diese Daten an den Server zu senden? Das resultierende Array ( $_POST) im serverseitigen PHP-Skript ist 0 ( NULL), wenn die Dateieingabe verwendet wird.

Ich weiß, dass dies möglich ist (obwohl ich bisher keine jQuery-Lösungen gefunden habe, nur Prototye-Code ( http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html )) ).

Dies scheint relativ neu zu sein, bitte erwähnen Sie nicht, dass das Hochladen von Dateien über XHR / Ajax unmöglich wäre, da es definitiv funktioniert.

Ich brauche die Funktionalität in Safari 5, FF und Chrome wäre nett, aber nicht unbedingt erforderlich.

Mein Code für jetzt ist:

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

Leider funktioniert die Verwendung des FormData-Objekts im IE <10 nicht.
Alejandro García Iglesias

@GarciaWebDev Angeblich können Sie eine Polyfüllung mit Flash verwenden, um dieselbe API zu unterstützen. Weitere Informationen finden Sie unter github.com/Modernizr/Modernizr/wiki/… .
Yuxhuang


3
Mit können $(':file')Sie alle Eingabedateien auswählen. Es ist nur ein bisschen einfacher.
Shahar

@RameshwarVyevhare Diese Antwort wurde fünf Jahre nach Beantwortung dieser Frage veröffentlicht. Bitte werfen Sie keine ähnlichen Fragen, nur um Ihre eigenen Antworten zu fördern.
Ryan P

Antworten:


882

Ab Safari 5 / Firefox 4 ist es am einfachsten, die FormDataKlasse zu verwenden:

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

Jetzt haben Sie ein FormDataObjekt, das zusammen mit XMLHttpRequest gesendet werden kann.

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

Es ist unbedingt erforderlich, dass Sie die contentTypeOption auf setzen falseund jQuery dazu zwingen, keinen Content-TypeHeader für Sie hinzuzufügen. Andernfalls fehlt die Begrenzungszeichenfolge. Außerdem müssen Sie das processDataFlag auf false setzen, andernfalls versucht jQuery, Ihr Flag FormDatain einen String umzuwandeln , was fehlschlägt.

Sie können die Datei jetzt in PHP abrufen mit:

$_FILES['file-0']

(Es gibt nur eine Datei, es file-0sei denn, Sie haben das multipleAttribut in Ihrer Dateieingabe angegeben. In diesem Fall werden die Zahlen mit jeder Datei erhöht.)

Verwenden der FormData-Emulation für ältere Browser

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

Erstellen Sie FormData aus einem vorhandenen Formular

Anstatt die Dateien manuell zu iterieren, kann das FormData-Objekt auch mit dem Inhalt eines vorhandenen Formularobjekts erstellt werden:

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

Verwenden Sie ein natives PHP-Array anstelle eines Zählers

Benennen Sie einfach Ihre Dateielemente gleich und beenden Sie den Namen in Klammern:

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

$_FILES['file']wird dann ein Array sein, das die Felder zum Hochladen von Dateien für jede hochgeladene Datei enthält. Ich empfehle dies gegenüber meiner ursprünglichen Lösung, da es einfacher ist, es zu wiederholen.


2
Außerdem gibt es eine FormData-Emulation, die das Portieren dieser Lösung auf ältere Browser recht einfach macht. Alles, was Sie tun müssen, ist, data.fakedie contentTypeEigenschaft manuell zu suchen und festzulegen sowie das zu verwendende xhr zu überschreiben sendAsBinary().
Raphael Schweikert

13
Sie verwenden also weder jQuerydie FormDataKlasse noch fragen Sie mich im Zusammenhang mit einer für jQuery spezifischen Frage und einer für die Verwendung von FormData spezifischen Antwort? Es tut mir leid, aber ich glaube nicht, dass ich Ihnen dort helfen kann ...
Raphael Schweikert

3
Dies verwendet application/x-www-form-urlencoded. Gibt es eine Möglichkeit, multipart/form-datastattdessen zu verwenden ?
Timmmm

4
@Timmmm Nein, es verwendet multipart/form-data. Verwenden application/x-www-form-urlencodedwürde nicht funktionieren.
Raphael Schweikert

5
@ RoyiNamir Es ist leider nur im Code dokumentiert .
Raphael Schweikert

51

Ich wollte nur ein bisschen zu Raphaels großartiger Antwort hinzufügen. Hier erfahren Sie, wie Sie PHP dazu bringen, dasselbe zu produzieren $_FILES, unabhängig davon, ob Sie JavaScript zum Senden verwenden.

HTML-Formular:

<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 erzeugt dies $_FILES, wenn es ohne JavaScript eingereicht wird:

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
                )

        )

)

Wenn Sie eine progressive Verbesserung durchführen, verwenden Sie Raphaels JS, um die Dateien zu senden ...

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

... so $_FILESsieht das PHP- Array aus, nachdem dieses JavaScript zum Senden verwendet wurde:

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
        )

)

Das ist ein schönes Array und tatsächlich das, $_FILESin das sich einige Leute verwandeln , aber ich finde es nützlich, mit demselben zu arbeiten $_FILES, unabhängig davon, ob JavaScript zum Senden verwendet wurde. Hier sind einige geringfügige Änderungen an der 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);
});

(Bearbeitung vom 14. April 2017: Ich habe das Formularelement aus dem Konstruktor von FormData () entfernt - das diesen Code in Safari behoben hat.)

Dieser Code macht zwei Dinge.

  1. Ruft das inputNamensattribut automatisch ab, wodurch der HTML-Code besser gewartet werden kann. Solange formdie Klasse putImages vorhanden ist, wird alles andere automatisch erledigt. Das heißt, die inputmüssen keinen speziellen Namen haben.
  2. Das Array-Format, das normales HTML übermittelt, wird vom JavaScript in der Zeile data.append neu erstellt. Beachten Sie die Klammern.

Mit diesen Änderungen erzeugt das Senden mit JavaScript jetzt genau das gleiche $_FILESArray wie das Senden mit einfachem HTML.


Hatte das gleiche Problem mit Safari. Danke für den Tipp!
Medoingthings

49

Schauen Sie sich meinen Code an, er erledigt den Job für mich

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

Dies hat perfekt funktioniert und ist sehr einfach zu implementieren.
Jh62

45

Ich habe diese Funktion gerade basierend auf einigen Informationen erstellt, die ich gelesen habe.

Verwenden Sie es wie verwenden .serialize(), stattdessen einfach setzen .serializefiles();.
Ich arbeite hier in meinen Tests.

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

2
Ich habe versucht, dies zum Laufen zu bringen, aber es schien serializefiles () nicht als Funktion zu erkennen, obwohl diese Definition oben auf der Seite steht.
Fallenreaper

1
das funktioniert bei mir ganz gut. Abrufen von Daten durch var data = $("#avatar-form").serializefiles();Senden über Ajax-Datenparameter und Analysieren mit Express Formidable: form.parse(req, function(err, fields, files){Vielen Dank für das Code-Snippet :)
SchurigH

23

Wenn Ihr Formular in Ihrem HTML-Code definiert ist, ist es einfacher, das Formular an den Konstruktor zu übergeben, als Bilder zu iterieren und hinzuzufügen.

$('#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){
            ...

9

Die Antwort von Devin Venable war nah an dem, was ich wollte, aber ich wollte eine, die auf mehreren Formularen funktioniert und die bereits im Formular angegebene Aktion verwendet, damit jede Datei an den richtigen Ort verschoben wird.

Ich wollte auch die on () -Methode von jQuery verwenden, um die Verwendung von .ready () zu vermeiden.

Das brachte mich dazu: (Ersetzen Sie formSelector durch Ihren jQuery-Selektor)

$(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.
    });

});


1

Die FormData-Klasse funktioniert, aber in iOS Safari (zumindest auf dem iPhone) konnte ich die Lösung von Raphael Schweikert nicht so verwenden, wie sie ist.

Mozilla Dev hat eine schöne Seite zum Bearbeiten von FormData-Objekten .

Fügen Sie also irgendwo auf Ihrer Seite ein leeres Formular hinzu und geben Sie den Enctype an:

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

Erstellen Sie dann das FormData-Objekt wie folgt:

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

und verfahren Sie wie in Raphaels Code .


Ich hatte ein Problem mit meinen Ajquery-Ajax-Uploads, die stillschweigend in Safari hängen, und führte schließlich eine browserbedingte $ ('Formularname'). Submit () für Safari anstelle des Ajax-Uploads durch, der in IE9 und FF18 funktioniert. Wahrscheinlich keine ideale Lösung für Multi-Uploads, aber ich habe dies für eine einzelne Datei in einem Iframe aus einem Abfragedialog getan, damit es in Ordnung war.
Glyphe

1

Ein Problem, auf das ich heute gestoßen bin, ist meines Erachtens im Zusammenhang mit diesem Problem erwähnenswert: Wenn die URL für den Ajax-Aufruf umgeleitet wird, kann der Header für den Inhaltstyp "Multipart / Formulardaten" verloren gehen.

Zum Beispiel habe ich auf http://server.com/context?param=x gepostet

Auf der Registerkarte "Netzwerk" von Chrome habe ich den richtigen mehrteiligen Header für diese Anforderung gesehen, aber dann eine 302-Weiterleitung zu http://server.com/context/?param=x (beachten Sie den Schrägstrich nach dem Kontext).

Während der Umleitung ging der mehrteilige Header verloren. Stellen Sie sicher, dass Anforderungen nicht umgeleitet werden, wenn diese Lösungen für Sie nicht funktionieren.


1

Alle oben genannten Lösungen sehen gut und elegant aus, aber das FormData () -Objekt erwartet keinen Parameter, verwendet jedoch append (), nachdem es instanziiert wurde, wie oben beschrieben:

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


1

Wenn die Dateieingabe nameein Array und Flags angibt multipleund Sie das gesamte formmit analysieren FormData, müssen append()die Eingabedateien nicht iterativ ausgeführt werden. FormDataverarbeitet automatisch mehrere Dateien.

$('#submit_1').on('click', function() {
  let data = new FormData($("#my_form")[0]);

  $.ajax({
    url: '/path/to/php_file',
    type: 'POST',
    data: data,
    processData: false,
    contentType: false,
    success: function(r) {
      console.log('success', r);
    },
    error: function(r) {
      console.log('error', r);
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="my_form">
  <input type="file" name="multi_img_file[]" id="multi_img_file" accept=".gif,.jpg,.jpeg,.png,.svg" multiple="multiple" />
  <button type="button" name="submit_1" id="submit_1">Not type='submit'</button>
</form>

Beachten Sie, dass kein reguläres button type="button"verwendet wird, nicht type="submit". Dies zeigt, dass keine Abhängigkeit von der Verwendung besteht submit, um diese Funktionalität zu erhalten.

Der resultierende $_FILESEintrag sieht in den Chrome-Entwicklungstools folgendermaßen aus:

multi_img_file:
  error: (2) [0, 0]
  name: (2) ["pic1.jpg", "pic2.jpg"]
  size: (2) [1978036, 2446180]
  tmp_name: (2) ["/tmp/phphnrdPz", "/tmp/phpBrGSZN"]
  type: (2) ["image/jpeg", "image/jpeg"]

Hinweis: Es gibt Fälle, in denen einige Bilder beim Hochladen als einzelne Datei einwandfrei hochgeladen werden, beim Hochladen in mehreren Dateien jedoch fehlschlagen. Das Symptom ist, dass PHP-Berichte leer sind $_POSTund $_FILESohne dass AJAX Fehler auslöst. Das Problem tritt bei Chrome 75.0.3770.100 und PHP 7.0 auf. Scheint nur mit 1 von mehreren Dutzend Bildern in meinem Testset zu passieren.


0

Ältere Versionen des IE unterstützen FormData nicht (die vollständige Liste der Browserunterstützung für FormData finden Sie hier: https://developer.mozilla.org/en-US/docs/Web/API/FormData ).

Entweder können Sie ein jquery-Plugin verwenden (z. B. http://malsup.com/jquery/form/#code-samples ) oder Sie können eine IFrame- basierte Lösung verwenden, um mehrteilige Formulardaten über ajax: https: // developer zu veröffentlichen. mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript


0

PHP

<?php 
    var_dump( $_FILES ); 
?>

jQuery und Form HTML

$( "#form" ).bind( "submit", function (e) {

    e.preventDefault();

    $.ajax({

        method: "post",
        url: "process.php",

        data: new FormData( $(this) ), // $(this) = formHTML element

        cache: false,

        /* upload */
        contentType: false,
        processData: false

    });

} )
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

<form id="form" >
    File: <input type="file" name="upload" />
    <input type="submit" value="Submit" />
</form>


-1
  1. Holen Sie sich das Formularobjekt mit jquery-> $ ("# id") [0]
  2. data = new FormData ($ ("# id") [0]);
  3. ok, Daten sind dein Wunsch

$ ("# id") [0] gibt zuerst keine leere <input type = "file" /> des Formulars zurück. Wie senden Sie das gesamte Formular einschließlich aller <input type = "file" /> davon?
Mohammad-Hossein Jamali
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.