Question Envoi multipart / formdata avec jQuery.ajax


J'ai un problème pour envoyer un fichier à un script PHP côté serveur en utilisant la fonction ajax de jQuery. Il est possible d'obtenir la liste de fichiers avec $('#fileinput').attr('files') mais comment est-il possible d'envoyer ces données au serveur? Le tableau résultant ($_POST) sur le serveur php-script est 0 (NULL) lors de l'utilisation de l'entrée de fichier.

Je sais que c'est possible (même si je n'ai trouvé aucune solution jQuery jusqu'à présent, seul le code Prototye (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 le téléchargement de fichiers serait impossible via XHR / Ajax, parce que ça fonctionne vraiment.

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

Mon code pour l'instant 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);
    }
});

475
2018-03-22 13:52


origine


Réponses:


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

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

Alors maintenant vous avez un FormData objet, prêt à être envoyé avec le 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 définissiez le contentType option à false, forçant jQuery à ne pas ajouter de Content-Type l'en-tête pour vous, sinon, la chaîne de délimitation sera manquante. De plus, vous devez laisser le processData flag vaut false, sinon jQuery va essayer de convertir FormData dans 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é multiple attribut sur votre entrée de fichier, dans ce cas, les nombres seront incrémentés avec chaque fichier.)

En utilisant le Émulation FormData pour les anciens navigateurs

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 également être créé avec le contenu d'un objet de formulaire existant:

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

Utiliser un tableau natif PHP au lieu d'un compteur

Il suffit de nommer les éléments de votre fichier et de mettre fin au nom 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 de téléchargement de fichiers pour chaque fichier téléchargé. En fait, je recommande ceci sur ma solution initiale, car il est plus facile d'itérer.


766
2018-05-12 09:36



Je viens de construire cette fonction en fonction de certaines informations que j'ai lues.

Utilisez-le comme en utilisant .serialize(), au lieu de mettre juste .serializefiles();.
Travailler 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
2017-09-14 14:33



Je voulais juste ajouter un peu à la bonne réponse de Raphaël. Voici comment faire en sorte que PHP produise le même $_FILES, que vous utilisiez JavaScript ou non pour le soumettre.

Formulaire 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 ceci $_FILES, lorsque 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 une amélioration progressive, utilisez 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 que PHP's $_FILES tableau ressemble, 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 dans, mais je trouve qu'il est utile de travailler avec le même $_FILES, peu importe si JavaScript a été utilisé pour soumettre. Donc, voici quelques changements mineurs au 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 formulaire du constructeur de FormData () - qui a corrigé ce code dans Safari.)

Ce code fait deux choses.

  1. Récupère le input Attribut de nom automatiquement, 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 n'a pas besoin d'avoir de nom particulier.
  2. Le format de tableau que HTML normal soumet est recréé par le JavaScript dans la ligne data.append. Notez les parenthèses.

Avec ces modifications, la soumission avec JavaScript produit désormais exactement le même $_FILES tableau comme soumission avec HTML simple.


43
2018-01-10 00:56



Regarde mon code, ça fait le travail pour moi

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

37
2017-08-30 06:49



Si votre formulaire est défini dans votre code HTML, il est plus facile de passer le formulaire dans le constructeur que d'itérer et d'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){
            ...

20
2018-06-02 17:43



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

Je voulais aussi utiliser la méthode on () de jQuery pour éviter d'utiliser .ready ().

Cela m'a amené à ceci: (remplacez 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.
    });

});

4
2018-06-15 16:54



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

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

Donc, ajoutez un formulaire vide quelque part dans votre page, en spécifiant l'enctype:

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

Ensuite, créez l'objet FormData en tant que:

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

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


1
2018-02-22 00:24