web-dev-qa-db-fra.com

Comment envoyer des objets FormData avec Ajax-request dans jQuery?

Le standard XMLHttpRequest Level 2 (un brouillon toujours actif) définit l'interface FormData. Cette interface permet d’ajouter des objets File aux requêtes XHR (requêtes Ajax).

Btw, c'est une nouvelle fonctionnalité - dans le passé, le "caché-iframe-astuce" était utilisé (lisez à 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 );

input est un champ <input type="file"> et handler est le gestionnaire de réussite pour la demande Ajax.

Cela fonctionne à merveille dans tous les navigateurs (encore une fois, sauf IE).

Maintenant, j'aimerais que cette fonctionnalité fonctionne 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 "Invocation illégale" est générée - screenshot is here ). Je suppose que jQuery attend un objet clé-valeur simple représentant les noms/valeurs de champs de formulaire, et que l'instance FormData que je transmets est apparemment incompatible.

Maintenant, puisqu'il est possible de passer une instance FormData dans 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 traqueur de bogues 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, laissez-moi vous présenter une démonstration démontrant le comportement que j’aimerais atteindre.

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 résulte en cette requête HTTP:

multipartformdata

C'est ce dont j'ai besoin - Je veux que 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 se traduit par:

wrongcontenttype

Comme vous pouvez le constater, le type de contenu est incorrect ...

452
Šime Vidas

Je crois que tu pourrais le faire comme ça:

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

Définir processData sur false vous empêche d'empêcher jQuery de transformer automatiquement les données en chaîne de requête. Voir la documentation pour plus d'informations.

Définir la valeur contentType sur false est impératif, car sinon jQuery le définira de manière incorrecte .

815
pradeek

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

Construire 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, lorsque vous obligez jQuery ajax à effectuer des tâches inattendues, l'événement beforeSend constitue un excellent endroit 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 pouvoir modifier à peu près n'importe quoi sur l'objet jqXHR dans l'événement before send.

28
BenSwayne

Vous pouvez envoyer l'objet FormData dans une requête ajax en utilisant le code suivant,

$("form#formElement").submit(function(){
    var formData = new FormData($(this)[0]);
});

Ceci est très similaire à la réponse acceptée mais constitue une réponse réelle pour le sujet de la question. Cela soumettra les éléments de formulaire automatiquement dans FormData et vous ne devrez pas ajouter manuellement les données à la variable FormData.

La méthode ajax ressemble à ceci,

$("form#formElement").submit(function(){
    var formData = new FormData($(this)[0]);
    //append some non-form data also
    formData.append('other_data',$("#someInputData").val());
    $.ajax({
        type: "POST",
        url: postDataUrl,
        data: formData,
        processData: false,
        contentType: false,
        dataType: "json",
        success: function(data, textStatus, jqXHR) {
           //process data
        },
        error: function(data, textStatus, jqXHR) {
           //process error msg
        },
});

Vous pouvez également transmettre manuellement l'élément de formulaire à l'intérieur de l'objet FormData en tant que paramètre, comme ceci:

var formElem = $("#formId");
var formdata = new FormData(form[0]);

J'espère que ça aide. ;)

18
Lucky

Vous pouvez utiliser l'événement $ .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

5
dmnkhhn

Je le fais comme ça et ça marche pour moi, j'espère que ça va aider :)

   <div id="data">
        <form>
            <input type="file" name="userfile" id="userfile" size="20" />
            <br /><br />
            <input type="button" id="upload" value="upload" />
        </form>
    </div>
  <script>
        $(document).ready(function(){
                $('#upload').click(function(){

                    console.log('upload button clicked!')
                    var fd = new FormData();    
                    fd.append( 'userfile', $('#userfile')[0].files[0]);

                    $.ajax({
                      url: 'upload/do_upload',
                      data: fd,
                      processData: false,
                      contentType: false,
                      type: 'POST',
                      success: function(data){
                        console.log('upload success!')
                        $('#data').empty();
                        $('#data').append(data);

                      }
                    });
                });
        });
    </script>   
5
talalalshehri

JavaScript:

function submitForm() {
    var data1 = new FormData($('input[name^="file"]'));
    $.each($('input[name^="file"]')[0].files, function(i, file) {
        data1.append(i, file);
    });

    $.ajax({
        url: "<?php echo base_url() ?>employee/dashboard2/test2",
        type: "POST",
        data: data1,
        enctype: 'multipart/form-data',
        processData: false, // tell jQuery not to process the data
        contentType: false // tell jQuery not to set contentType
    }).done(function(data) {
        console.log("PHP Output:");
        console.log(data);
    });
    return false;
}

PHP:

public function upload_file() {
    foreach($_FILES as $key) {
        $name = time().$key['name'];
        $path = 'upload/'.$name;
        @move_uploaded_file($key['tmp_name'], $path);
    }
}
3
Rahul Yadav

Au lieu de - fd.append( 'userfile', $('#userfile')[0].files[0]);

Utiliser - fd.append( 'file', $('#userfile')[0].files[0]);

2
user4757231

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

Exemples http://jquery.malsup.com/form/#ajaxSubmit

vue approximative:

<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>
2
Rohit