556 votes

Comment envoyer des objets FormData avec des requêtes Ajax en jQuery ?

Le site XMLHttpRequest Niveau 2 (encore à l'état de projet) définit les FormData interface. Cette interface permet d'ajouter File aux requêtes XHR (requêtes Ajax).

En fait, il s'agit d'une nouvelle fonctionnalité - dans le passé, l'astuce du "cadre caché" était utilisée (lire à ce sujet dans mon autre question ).

Voici comment cela fonctionne (exemple) :

var xhr = new XMLHttpRequest(),
    fd = new FormData();

fd.append( 'file', input.files[0] );
xhr.open( 'POST', 'http://example.com/script.php', true );
xhr.onreadystatechange = handler;
xhr.send( fd );

donde input es un <input type="file"> et handler est le gestionnaire de succès pour la requête Ajax.

Cela fonctionne parfaitement dans tous les navigateurs (à nouveau, sauf IE).

Maintenant, je voudrais faire fonctionner cette fonctionnalité avec jQuery. J'ai essayé ceci :

var fd = new FormData();    
fd.append( 'file', input.files[0] );

$.post( 'http://example.com/script.php', fd, handler );

Malheureusement, cela ne fonctionnera pas (une erreur "Illegal invocation" est émise). La capture d'écran est ici ). Je suppose que jQuery s'attend à un simple objet clé-valeur représentant les noms / valeurs des champs de formulaire, et que l'objet FormData que je passe dans l'instance est apparemment incompatible.

Maintenant, puisqu'il est possible de passer un FormData en xhr.send() J'espère qu'il est également possible de le faire fonctionner avec jQuery.


Mise à jour :

J'ai créé un "ticket de fonctionnalité" sur le Bug Tracker de jQuery. C'est ici : http://bugs.jquery.com/ticket/9995

On m'a suggéré d'utiliser un "préfiltre Ajax"...


Mise à jour :

Tout d'abord, permettez-moi de faire une démonstration du comportement que je souhaite obtenir.

HTML :

<form>
    <input type="file" id="file" name="file">
    <input type="submit">
</form>

JavaScript :

$( 'form' ).submit(function ( e ) {
    var data, xhr;

    data = new FormData();
    data.append( 'file', $( '#file' )[0].files[0] );

    xhr = new XMLHttpRequest();

    xhr.open( 'POST', 'http://hacheck.tel.fer.hr/xml.pl', true );
    xhr.onreadystatechange = function ( response ) {};
    xhr.send( data );

    e.preventDefault();
});

Le code ci-dessus donne lieu à cette requête HTTP :

multipartformdata

Voici ce dont j'ai besoin - Je veux ce type de contenu "multipart/form-data" !


La solution proposée serait la suivante :

$( 'form' ).submit(function ( e ) {
    var data;

    data = new FormData();
    data.append( 'file', $( '#file' )[0].files[0] );

    $.ajax({
        url: 'http://hacheck.tel.fer.hr/xml.pl',
        data: data,
        processData: false,
        type: 'POST',
        success: function ( data ) {
            alert( data );
        }
    });

    e.preventDefault();
});

Cependant, cela a pour conséquence :

wrongcontenttype

Comme vous pouvez le voir, le type de contenu est faux...

1 votes

OUI ! c'est une excellente idée ! merci aussi pour l'info. J'ai récemment trouvé un snippet qui fait cela mais ce n'est pas jQuery et je me demandais si jQuery pouvait le faire. C'est plus une réponse qu'une question pour moi. Tenez-nous au courant !

0 votes

Maintenant, si seulement je savais quoi mettre dans ce préfiltre.

1 votes

Même situation ici. Quelqu'un a-t-il d'autres indices ?

986voto

pradeek Points 6137

Je crois que vous pourriez le faire comme ceci :

var fd = new FormData();    
fd.append( 'file', input.files[0] );

$.ajax({
  url: 'http://example.com/script.php',
  data: fd,
  processData: false,
  contentType: false,
  type: 'POST',
  success: function(data){
    alert(data);
  }
});

Notes :

  • Réglage de processData a false vous permet d'empêcher jQuery de transformer automatiquement les données en une chaîne de requête. Voir les docs pour plus d'informations.

  • Fixer le contentType a false est impératif, car sinon jQuery le réglera de manière incorrecte .

0 votes

Malheureusement, le résultat est que l'en-tête HTTP-request "Content-Type" n'est pas le bon. Voir ma question pour une explication détaillée de ce que je veux réaliser et pourquoi votre solution actuelle ne me satisfait pas. Peut-être puis-je définir manuellement le type de contenu ?

12 votes

Oui, je crois que vous pouvez définir manuellement le contentType en "multipart/form-data" en ajoutant une paire clé-valeur dans l'argument $.ajax.

0 votes

Bonjour @pradeek, nous avons essayé cette solution ici ( stackoverflow.com/questions/10215425/ ), mais la façon de transmettre les données de l'image dans le corps de la requête POST n'était pas claire. Avez-vous des idées ? Merci !

31voto

BenSwayne Points 10621

Il y a quelques techniques encore à mentionner qui sont disponibles pour vous. Commencez par définir la propriété contentType dans vos paramètres ajax.

En s'appuyant sur l'exemple de Pradeek :

$('form').submit(function (e) {
    var data;

    data = new FormData();
    data.append('file', $('#file')[0].files[0]);

    $.ajax({
        url: 'http://hacheck.tel.fer.hr/xml.pl',
        data: data,
        processData: false,
        type: 'POST',

        // This will override the content type header, 
        // regardless of whether content is actually sent.
        // Defaults to 'application/x-www-form-urlencoded'
        contentType: 'multipart/form-data', 

        //Before 1.5.1 you had to do this:
        beforeSend: function (x) {
            if (x && x.overrideMimeType) {
                x.overrideMimeType("multipart/form-data");
            }
        },
        // Now you should be able to do this:
        mimeType: 'multipart/form-data',    //Property added in 1.5.1

        success: function (data) {
            alert(data);
        }
    });

    e.preventDefault();
});

Dans certains cas, lorsqu'on force jQuery ajax à faire des choses non attendues, la fonction beforeSend est un endroit idéal pour le faire. Pendant un certain temps, les gens utilisaient beforeSend pour remplacer le mimeType avant que celui-ci ne soit ajouté à jQuery dans la version 1.5.1. Vous devriez être en mesure de modifier à peu près tout ce qui se trouve sur l'objet jqXHR dans l'événement "before send".

5 votes

Il y a un problème avec cela : la spécification exige qu'un type de contenu multipart comprenne le paramètre de frontière (spécifiquement, il est indiqué comme un requerido paramètre).

1 votes

@romkyns J'ai codé ça du haut de ma tête. Si vous avez une expérience ou des connaissances que je n'ai pas, n'hésitez pas à modifier la réponse ! Maintenant que vous l'avez mentionné, une recherche rapide a permis de trouver ceci : stackoverflow.com/questions/5933949/ ce qui devrait vous permettre d'avancer dans la bonne direction. (Vous pourriez prendre cet exemple et l'incorporer dans le jQuery ci-dessus si cela vous intéresse).

0 votes

En fait, la réponse actuellement acceptée (que j'ai modifiée pour ajouter une correction cruciale) fait enfin ce qu'il faut. C'était délicat :)

4voto

dmnkhhn Points 231

Vous pouvez utiliser le $.ajax beforeSend pour manipuler l'en-tête.

beforeSend: function(xhr) { 
    xhr.setRequestHeader('Content-Type', 'multipart/form-data');
}

Voir ce lien pour plus d'informations : http://msdn.microsoft.com/en-us/library/ms536752(v=vs.85).aspx

4voto

nivanka Points 533

Je pense que vous ne pouvez pas le faire en ajax pour supporter tous les navigateurs, je dirais que c'est bien de vérifier ce plugin ajax uploader pour voir comment ils l'ont fait. http://valums.com/ajax-upload/

1voto

Rohit Points 149

Si vous voulez soumettre des fichiers en utilisant l'ajax, utilisez "jquery.form.js". Cela soumet tous les éléments de formulaire facilement.

Échantillons http://jquery.malsup.com/form/#ajaxSubmit

vue d'ensemble :

<form id='AddPhotoForm' method='post' action='../photo/admin_save_photo.php' enctype='multipart/form-data'>

<script type="text/javascript">
function showResponseAfterAddPhoto(responseText, statusText)
{ 
    information= responseText;
    callAjaxtolist();
    $("#AddPhotoForm").resetForm();
    $("#photo_msg").html('<div class="album_msg">Photo uploaded Successfully...</div>');        
};

$(document).ready(function(){
    $('.add_new_photo_div').live('click',function(){
            var options = {success:showResponseAfterAddPhoto};  
            $("#AddPhotoForm").ajaxSubmit(options);
        });
});
</script>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X