Envoi de multipart / formdata avec jQuery.Ajax

j'ai un problème pour envoyer un fichier à un script PHP serverside en utilisant la fonction ajax de jQuery. Il est possible d'obtenir la liste des fichiers avec $('#fileinput').attr('files') mais comment envoyer ces données au serveur? Le tableau résultant ( $_POST ) sur le script php serverside est 0 ( NULL ) en utilisant le fichier-input.

je sais que c'est possible (bien que je n'ai pas trouvé de solutions jQuery jusqu'à maintenant, seulement Prototye code ( http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html )).

cela semble être relativement nouveau, alors s'il vous plaît ne pas mentionner que le téléchargement de fichier serait impossible via XHR/Ajax, parce que cela fonctionne certainement.

J'ai besoin de la fonctionnalité dans Safari 5, FF et Chrome serait agréable, mais ne sont pas essentiels.

mon code pour le moment est:

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

11 réponses

à partir de Safari 5 / Firefox 4, il est plus facile d'utiliser la classe FormData :

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

donc maintenant vous avez un objet FormData , prêt à être envoyé avec 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);
    }
});

il est impératif que vous positionniez l'option contentType sur false , forçant jQuery à ne pas ajouter d'en-tête Content-Type pour vous, sinon, la chaîne de limite sera manquante. Aussi, vous devez quitter le processData drapeau mis à false, sinon, jQuery essaiera de convertir votre FormData en une chaîne, qui échouera.

vous pouvez maintenant récupérer le fichier en PHP en utilisant:

$_FILES['file-0']

(il n'y a qu'un seul fichier, file-0 , sauf si vous avez spécifié l'attribut multiple sur votre entrée de fichier, auquel cas, les nombres vont augmenter avec chaque fichier.)

utilisant le FormData emulation pour navigateurs plus anciens

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

créer FormData à partir d'un formulaire existant

au lieu d'itérer manuellement les fichiers, L'objet FormData peut aussi être créé avec le contenu d'un objet form:

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

utiliser un tableau natif PHP au lieu d'un compteur

il suffit de nommer vos éléments de fichier les mêmes et de terminer le nom dans entre parenthèses:

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

$_FILES['file'] sera alors un tableau contenant les champs d'upload de fichier pour chaque fichier téléchargé. En fait, je recommande cela par rapport à ma solution initiale car il est plus simple d'itérer plus.

782
répondu Raphael Schweikert 2017-10-05 20:13:40

voulait juste ajouter un peu à la grande réponse de Raphaël. Voici comment faire pour que PHP produise le même $_FILES , peu importe si vous utilisez JavaScript pour soumettre.

format 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 produit ce $_FILES , lorsqu'il est soumis sans 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
                )

        )

)

si vous faites l'amélioration progressive, en utilisant le JS de Raphael pour soumettre les fichiers...

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

... c'est ce à quoi ressemble le tableau $_FILES de PHP, après avoir utilisé ce JavaScript pour soumettre:

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
        )

)

C'est un joli tableau, et en fait ce que certaines personnes transforment $_FILES En, mais je trouve qu'il est utile de travailler avec le même $_FILES , peu importe si JavaScript a été utilisé pour soumettre. Ainsi, voici quelques changements mineurs à la 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 avril 2017 edit: j'ai supprimé l'élément de forme du constructeur de FormData () -- that fixe ce code dans Safari.)

ce code fait deux choses.

  1. récupère automatiquement l'attribut de nom input , ce qui rend le HTML plus maintenable. Maintenant, aussi longtemps que form a la classe putImages, tout le reste est pris en charge automatiquement. C'est, le input pas besoin d'avoir de nom.
  2. le format de tableau que le HTML normal soumet est recréé par le JavaScript dans le données.ajouter ligne. Notez les parenthèses.

avec ces modifications, soumettre avec JavaScript produit maintenant exactement le même tableau $_FILES que soumettre avec HTML simple.

46
répondu ajmicek 2017-04-14 17:00:50

je viens de construire cette fonction basée sur des informations que j'ai lues.

utilisez-le comme si vous utilisiez .serialize() , à la place mettez .serializefiles(); .

Je travaille ici dans mes 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);
43
répondu evandro777 2018-10-10 17:01:07

Regardez mon code, il fait le travail pour moi

$( '#formId' )
  .submit( function( e ) {
    $.ajax( {
      url: 'FormSubmitUrl',
      type: 'POST',
      data: new FormData( this ),
      processData: false,
      contentType: false
    } );
    e.preventDefault();
  } );
39
répondu Asad Malik 2014-11-04 05:06:22

si votre forme est définie dans votre HTML, il est plus facile de passer la forme dans le constructeur que d'itérer et ajouter des images.

$('#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
répondu Devin Venable 2014-06-02 17:43:39

la réponse de Devin Venable était proche de ce que je voulais, mais j'en voulais un qui fonctionnerait sur plusieurs formulaires, et utiliserait l'action déjà spécifiée dans le formulaire pour que chaque fichier aille au bon endroit.

je voulais aussi utiliser la méthode on() de jQuery pour éviter de l'utiliser .prêt.)(

qui m'a amené à ceci: (remplacer formSelector par votre sélecteur 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
répondu Karl Henselin 2017-05-23 12:34:45

la classe FormData fonctionne, cependant dans iOS Safari (sur iPhone au moins) je n'ai pas pu utiliser la solution de Raphael Schweikert telle quelle.

Mozilla Dev a une belle page sur la manipulation des FormData objets .

ainsi, ajoutez un formulaire vide quelque part dans votre page, en spécifiant le type d'enctype:

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

ensuite, créer L'objet FormData comme:

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

et procéder comme dans code de Raphaël .

1
répondu topkara 2017-05-23 12:34:45

un gotcha que j'ai rencontré aujourd'hui je pense qu'il vaut la peine de souligner lié à ce problème: si l'url pour l'appel ajax est redirigé alors l'en-tête pour le type de contenu: 'multipart/form-data' peut être perdu.

par exemple, je postais sur http://server.com/context?param=x

dans L'onglet Réseau de Chrome j'ai vu l'en-tête multipart correct pour cette requête mais ensuite une redirection 302 à http://server.com/context/?param=x (noter la barre oblique après le contexte)

pendant la redirection l'en-tête multipart a été perdu. Assurer les demandes ne sont pas redirigés si ces solutions ne fonctionnent pas pour vous.

0
répondu james 2016-03-17 22:07:16

les versions plus anciennes D'IE ne prennent pas en charge FormData ( la liste complète de la prise en charge de FormData par le navigateur est ici: https://developer.mozilla.org/en-US/docs/Web/API/FormData ).

soit vous pouvez utiliser un plugin jquery (par exemple, http://malsup.com/jquery/form/#code-samples ) ou, vous pouvez utiliser une solution basée sur IFrame pour post multipart form data via ajax: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript

0
répondu sudip 2018-02-08 10:42:19

toutes les solutions ci-dessus semblent bonnes et élégantes, mais L'objet FormData() n'attend aucun paramètre, mais utilise append() après l'instanciation, comme ce qu'on a écrit ci-dessus:

formData.annexe (val.nom, val.valeur);

0
répondu szatti1489 2018-03-08 13:47:10
  1. obtenez de l'objet de formulaire en jquery-> $("#id")[0]
  2. data = new FormData ($("#id") [0]);
  3. ok,les données que vous voulez
-1
répondu user1909226 2013-07-18 10:11:39