it-swarm.com.de

Senden von multipart/formdata mit jQuery.ajax

Ich habe ein Problem beim Senden einer Datei an ein serverseitiges PHP-Skript mit der jjery-Funktion ajax-function . Es ist möglich, die Dateiliste mit $('#fileinput').attr('files') abzurufen, aber wie können diese Daten an den Server gesendet werden? 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. Erwähnen Sie bitte nicht, dass das Hochladen von Dateien über XHR/Ajax unmöglich ist, da es definitiv funktioniert.

Ich brauche die Funktionalität in Safari 5, FF und Chrome wären zwar nett, aber nicht zwingend 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);
    }
});
504
zoku

Ab Safari 5/Firefox 4 ist es am einfachsten, die FormData-Klasse zu verwenden:

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

Jetzt haben Sie ein FormData-Objekt, 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);
    }
});

Sie müssen die Option contentType unbedingt auf false setzen, sodass jQuery keinen Content-Type-Header für Sie hinzufügen muss. Andernfalls fehlt die Begrenzungszeichenfolge .. processData-Flag muss auf false festgelegt sein. jQuery versucht, FormData in eine Zeichenfolge umzuwandeln, die fehlschlägt.

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

$_FILES['file-0']

(Es gibt nur eine Datei, file-0, sofern Sie bei Ihrer Dateieingabe nicht das multiple-Attribut angegeben haben. 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);

FormData aus einem vorhandenen Formular erstellen

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

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

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

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

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

$_FILES['file'] ist dann ein Array mit den Feldern zum Hochladen der Datei für jede hochgeladene Datei. Ich empfehle dies tatsächlich gegenüber meiner ursprünglichen Lösung, da es einfacher ist, das Programm zu wiederholen.

814

Ich wollte nur Raffaels großartige Antwort etwas hinzufügen. So können Sie mit PHP denselben $_FILES erstellen, unabhängig davon, ob Sie zum Senden JavaScript 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 diesen $_FILES, wenn er ohne JavaScript übermittelt 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 progressive Verbesserungen 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 sieht das $_FILES-Array von PHP aus, nachdem dieses JavaScript zum Übergeben 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 Nice-Array, und tatsächlich verwandeln einige Leute $_FILES in, aber ich finde es nützlich, mit demselben $_FILES zu arbeiten, unabhängig davon, ob zum Senden JavaScript verwendet wurde. Hier sind einige kleinere Ä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);
});

(14. April 2017 edit: Ich habe das Formularelement aus dem Konstruktor von FormData () entfernt - dadurch wurde dieser Code in Safari behoben.)

Dieser Code macht zwei Dinge.

  1. Ruft das input name-Attribut automatisch ab, sodass der HTML-Code leichter zu verwalten ist. Solange form die Klasse putImages hat, wird alles andere automatisch erledigt. Das heißt, die input muss keinen besonderen Namen haben.
  2. Das Array-Format, das normaler HTML-Code übermittelt, wird von JavaScript in der Zeile data.append neu erstellt. Beachten Sie die Klammern.

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

46
ajmicek

Schau dir meinen Code an, er erledigt die Arbeit für mich

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

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

Verwenden Sie es wie .serialize(), stattdessen .serializefiles();.
Arbeit 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);
44
evandro777

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){
            ...
23
Devin Venable

Devin Venables Antwort war nahe an dem, was ich wollte, aber ich wollte eines, das an mehreren Formularen arbeiten würde und die bereits im Formular angegebene Aktion verwenden würde, damit jede Datei an die richtige Stelle geht.

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

Das brachte mich dazu: (Ersetze formSelector durch deinen jQuery-Selector)

$(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

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

Mozilla Dev hat eine schöne Seite zur Bearbeitung 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 als:

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

und verfahren Sie wie in Raphaels Code .

1
topkara

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

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

0
sudip

Wenn die Dateieingabe name ein Array und Flags multiple angibt und Sie das gesamte form mit FormData analysieren, ist es nicht erforderlich, die Eingabe iterativ append() durchzuführen Dateien. FormData verarbeitet 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 ein regulärer button type="button" verwendet wird, nicht type="submit". Dies zeigt, dass keine Abhängigkeit von der Verwendung von submit besteht, um diese Funktionalität zu erhalten.

Der resultierende Eintrag $_FILES sieht in den Chrome Dev-Tools 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: In einigen Fällen können einige Bilder problemlos als einzelne Datei hochgeladen werden, sie schlagen jedoch fehl, wenn sie in mehreren Dateien hochgeladen werden. Das Symptom ist, dass PHP leere $_POST und $_FILES meldet, ohne dass AJAX Fehler verursacht. Problem tritt mit Chrome 75.0.3770.100 und PHP 7.0 auf. Scheint nur mit 1 von mehreren Dutzend Bildern in meinem Testset zu passieren.

0
OXiGEN

Nova Tage brauchen Sie nicht einmal jQuery :) API-Unterstützungstabelle abrufen

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

Ein Problem, auf das ich heute gestoßen bin, ist meines Erachtens in Bezug auf dieses Problem hervorzuheben: Wenn die URL für den Ajax-Aufruf umgeleitet wird, kann der Header für den Inhaltstyp "Multipart/Form-Daten" 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, dann aber eine 302-Weiterleitung zu http://server.com/context/?param=x (Beachten Sie den Schrägstrich hinter dem Kontext)

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

0
james

Alle oben genannten Lösungen sehen gut und elegant aus, das FormData () -Objekt erwartet jedoch keinen Parameter. Verwenden Sie danach append (), um es zu instanziieren, wie es oben beschrieben wurde:

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

0
szatti1489