Télécharger à la fois des données et des fichiers sous un seul formulaire en utilisant Ajax?

385

J'utilise jQuery et Ajax pour mes formulaires pour soumettre des données et des fichiers mais je ne sais pas comment envoyer à la fois des données et des fichiers dans un seul formulaire?

Actuellement, je fais presque la même chose avec les deux méthodes, mais la façon dont les données sont collectées dans un tableau est différente, les données utilisent .serialize();mais les fichiers utilisent= new FormData($(this)[0]);

Est-il possible de combiner les deux méthodes pour pouvoir télécharger des fichiers et des données sous une forme via Ajax?

Données jQuery, Ajax et html

$("form#data").submit(function(){

    var formData = $(this).serialize();

    $.ajax({
        url: window.location.pathname,
        type: 'POST',
        data: formData,
        async: false,
        success: function (data) {
            alert(data)
        },
        cache: false,
        contentType: false,
        processData: false
    });

    return false;
});

<form id="data" method="post">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <button>Submit</button>
</form>

Fichiers jQuery, Ajax et html

$("form#files").submit(function(){

    var formData = new FormData($(this)[0]);

    $.ajax({
        url: window.location.pathname,
        type: 'POST',
        data: formData,
        async: false,
        success: function (data) {
            alert(data)
        },
        cache: false,
        contentType: false,
        processData: false
    });

    return false;
});

<form id="files" method="post" enctype="multipart/form-data">
    <input name="image" type="file" />
    <button>Submit</button>
</form>

Comment puis-je combiner ce qui précède afin que je puisse envoyer des données et des fichiers sous une forme via Ajax?

Mon objectif est de pouvoir envoyer tout ce formulaire en un seul message avec Ajax, est-ce possible?

<form id="datafiles" method="post" enctype="multipart/form-data">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <input name="image" type="file" />
    <button>Submit</button>
</form>
Dan
la source
2
L' FormDataapproche devrait bien fonctionner avec des formulaires qui contiennent tout ce que vous voulez, pas seulement les champs de téléchargement de fichiers; il n'est cependant pas largement pris en charge.
lanzz
@lanzz qui cependant? celui avec sérialiser semble fonctionner uniquement pour les données mais l'autre ne semble fonctionner que pour les fichiers?
Dan
À en juger par cette page MDN , toutes les données du formulaire doivent être soumises lorsque vous utilisezFormData
lanzz
1
@lanzz vous avez raison, cela fonctionne comme je le pensais, j'utilisais le mauvais identifiant de formulaire, vous pouvez télécharger à la fois des fichiers et des données via un formulaire avec ajax.
Dan
Cela ne semble pas fonctionner en cas d'entrée de fichier à sélection multiple. Il télécharge uniquement le premier fichier.
Sami Al-Subhi

Réponses:

459

Le problème que j'ai eu était d'utiliser le mauvais identifiant jQuery.

Vous pouvez télécharger des données et des fichiers avec un seul formulaire en utilisant ajax .

PHP + HTML

<?php

print_r($_POST);
print_r($_FILES);
?>

<form id="data" method="post" enctype="multipart/form-data">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <input name="image" type="file" />
    <button>Submit</button>
</form>

jQuery + Ajax

$("form#data").submit(function(e) {
    e.preventDefault();    
    var formData = new FormData(this);

    $.ajax({
        url: window.location.pathname,
        type: 'POST',
        data: formData,
        success: function (data) {
            alert(data)
        },
        cache: false,
        contentType: false,
        processData: false
    });
});

Version courte

$("form#data").submit(function(e) {
    e.preventDefault();
    var formData = new FormData(this);    

    $.post($(this).attr("action"), formData, function(data) {
        alert(data);
    });
});
Dan
la source
17
dans les versions d'IE <10, cette solution ne fonctionnera pas, car FormData est un objet HTML5, non présent dans IE 8 ou 9.
Xavier Guzman
34
$(this)[0]est juste un alias de this, new FormData(this)devrait donc être suffisant.
r3wt
9
Il ne semble pas possible d'inspecter l'objet FormData, voir cette question (pour tous ceux qui rencontrent la même ignorance que moi parce que l'objet était toujours vide).
Laura
28
Pour les futurs lecteurs: les déclarations contentType et processData sont importantes. Voir cette réponse pour plus d'informations.
AaronSieb
5
le async: falsene semble pas nécessaire pour que cela fonctionne et provoque un blocage sur les navigateurs mobiles (à un seul thread)
Jeremy Daalder
34

une autre option consiste à utiliser un iframe et à y définir la cible du formulaire.

vous pouvez essayer ceci (il utilise jQuery):

function ajax_form($form, on_complete)
{
    var iframe;

    if (!$form.attr('target'))
    {
        //create a unique iframe for the form
        iframe = $("<iframe></iframe>").attr('name', 'ajax_form_' + Math.floor(Math.random() * 999999)).hide().appendTo($('body'));
        $form.attr('target', iframe.attr('name'));
    }

    if (on_complete)
    {
        iframe = iframe || $('iframe[name="' + $form.attr('target') + '"]');
        iframe.load(function ()
        {
            //get the server response
            var response = iframe.contents().find('body').text();
            on_complete(response);
        });
    }
}

cela fonctionne bien avec tous les navigateurs, vous n'avez pas besoin de sérialiser ou de préparer les données. un inconvénient est que vous ne pouvez pas suivre les progrès.

aussi, au moins pour Chrome, la requête n'apparaîtra pas dans l'onglet "xhr" des outils de développement mais sous "doc"

Roey
la source
1
En effet ce n'est pas Ajax, ça peut quand même être utile aux personnes ayant la même question.
Roey
3
Je n'arrive pas à croire pourquoi cette réponse obtient -2, j'ai fini par l'utiliser car j'avais besoin d'une assistance pour les anciens navigateurs
Sijav
Cette réponse devrait être dans le fil car d'autres réponses notent «les anciens navigateurs ne fonctionnent pas» ou «un piratage iframe pourrait être utilisé» mais ne les traite jamais. Beau morceau de code, montrant également comment utiliser correctement onload +1
mschr
18

J'avais ce même problème dans ASP.Net MVC avec HttpPostedFilebase et au lieu d'utiliser le formulaire sur Soumettre, je devais utiliser le bouton sur le clic où je devais faire des choses et puis si tout allait bien le formulaire de soumission, voici comment je l'ai fait fonctionner

$(".submitbtn").on("click", function(e) {

    var form = $("#Form");

    // you can't pass Jquery form it has to be javascript form object
    var formData = new FormData(form[0]);

    //if you only need to upload files then 
    //Grab the File upload control and append each file manually to FormData
    //var files = form.find("#fileupload")[0].files;

    //$.each(files, function() {
    //  var file = $(this);
    //  formData.append(file[0].name, file[0]);
    //});

    if ($(form).valid()) {
        $.ajax({
            type: "POST",
            url: $(form).prop("action"),
            //dataType: 'json', //not sure but works for me without this
            data: formData,
            contentType: false, //this is requireded please see answers above
            processData: false, //this is requireded please see answers above
            //cache: false, //not sure but works for me without this
            error   : ErrorHandler,
            success : successHandler
        });
    }
});

cela remplira correctement votre modèle MVC, assurez-vous que dans votre modèle, la propriété de HttpPostedFileBase [] a le même nom que le nom du contrôle d'entrée en html, c'est-à-dire

<input id="fileupload" type="file" name="UploadedFiles" multiple>

public class MyViewModel
{
    public HttpPostedFileBase[] UploadedFiles { get; set; }
}
h_power11
la source
1
Vous êtes un gain de temps. :)
Suhail Mumtaz Awan
Dans mon cas j'ai dû utiliser:contentType : "application/octet-stream"
Christophe Roussy
Merci mon pote! Vous avez gagné beaucoup de temps.
Accès refusé
Ça marche avec Django, sympa!
csandreas1
Merci mon pote! Les 2 lignes ci-dessous ont fonctionné pour moi. var form = $ ("# Form"); var formData = new FormData (formulaire [0]);
Rajiv Kumar
15

Ou plus court:

$("form#data").submit(function() {
    var formData = new FormData(this);
    $.post($(this).attr("action"), formData, function() {
        // success    
    });
    return false;
});
Schaenk
la source
Donc, avec cela, comment validez-vous un champ de données en utilisant le même script, c'est-à-dire si vous avez un champ de texte et un champ de fichier dans votre formulaire
George
6

Pour moi, ça n'a pas marché sans enctype: 'multipart/form-data' champ dans la demande Ajax. J'espère que cela aide quelqu'un qui est coincé dans un problème similaire.

Même si le enctype était déjà défini dans l'attribut form , pour une raison quelconque, la requête Ajax n'a pas identifié automatiquement la enctypedéclaration sans explicite (jQuery 3.3.1).

// Tested, this works for me (jQuery 3.3.1)

fileUploadForm.submit(function (e) {   
    e.preventDefault();
    $.ajax({
            type: 'POST',
            url: $(this).attr('action'),
            enctype: 'multipart/form-data',
            data: new FormData(this),
            processData: false,
            contentType: false,
            success: function (data) {
                console.log('Thank God it worked!');
            }
        }
    );
});

// enctype field was set in the form but Ajax request didn't set it by default.

<form action="process/file-upload" enctype="multipart/form-data" method="post" >

     <input type="file" name="input-file" accept="text/plain" required> 
     ...
</form>

Comme d'autres mentionnés ci-dessus, veuillez également porter une attention particulière aux champs contentTypeet processData.

Adithya Upadhya
la source
1
"Pour moi, cela n'a pas fonctionné sans enctype: champ 'multipart / form-data' dans la requête Ajax." - Ça ne peut pas avoir d'effet. Ce n'est pas une propriété reconnue par jQuery.ajax. Voir la documentationenctypen'est pas mentionné du tout.
Quentin
Comme je l'ai mentionné précédemment, j'ai essayé plusieurs réponses différentes, mais elles n'ont pas fonctionné. Une erreur Ajax indiquant une erreur de codage a été affichée dans la console JS. Plus tard, j'ai suivi ce tutoriel qui a finalement fait fonctionner mon code et je l'ai posté ici. Peut-être que le enctypechamp n'est pas couvert dans la documentation pour une raison. Je n'ai pas vérifié le code source de jQuery, je ne peux donc pas le dire avec certitude.
Adithya Upadhya
"Le champ enctype n'est peut-être pas couvert dans la documentation pour une raison." - Cette raison est que jQuery ne fait rien avec elle donc c'est un non-sens.
Quentin
Vous pourriez peut-être contacter Mkyong et avoir une conversation avec lui à la place. J'ai testé à nouveau mon code en supprimant le enctypechamp et il ne télécharge plus de fichiers (erreur de type de codage renvoie). Je ne sais pas comment cela fonctionne car je n'ai pas vérifié le code source de jQuery. J'ai posté cette réponse avec l'intention d'aider ceux qui sont coincés dans un problème similaire. Je ne pêche pas les votes positifs ici ... Si vous avez d'autres questions / commentaires, discutons au lieu de commenter.
Adithya Upadhya
1

Pour moi, suivre le travail de code

$(function () {
    debugger;
    document.getElementById("FormId").addEventListener("submit", function (e) {
        debugger;
        if (ValidDateFrom()) { // Check Validation 
            var form = e.target;
            if (form.getAttribute("enctype") === "multipart/form-data") {
                debugger;
                if (form.dataset.ajax) {
                    e.preventDefault();
                    e.stopImmediatePropagation();
                    var xhr = new XMLHttpRequest();
                    xhr.open(form.method, form.action);
                    xhr.onreadystatechange = function (result) {
                        debugger;
                        if (xhr.readyState == 4 && xhr.status == 200) {
                            debugger;
                            var responseData = JSON.parse(xhr.responseText);
                            SuccessMethod(responseData); // Redirect to your Success method 
                        }
                    };
                    xhr.send(new FormData(form));
                }
            }
        }
    }, true);
});

Dans votre méthode de publication d'action, transmettez le paramètre en tant que HttpPostedFileBase UploadFile et assurez-vous que l'entrée de votre fichier est identique à celle mentionnée dans votre paramètre de la méthode d'action. Cela devrait également fonctionner avec le formulaire AJAX Begin.

Rappelez-vous ici que votre formulaire AJAX BEGIN ne fonctionnera pas ici puisque vous effectuez votre appel de poste défini dans le code mentionné ci-dessus et vous pouvez référencer votre méthode dans le code selon l'exigence

Je sais que je réponds tard mais c'est ce qui a fonctionné pour moi

Pranav Kulshrestha
la source
1

Une manière simple mais plus efficace:
new FormData()est elle-même comme un conteneur (ou un sac). Vous pouvez mettre tout attr ou fichier en lui-même. La seule chose dont vous aurez besoin pour ajouter attribute, file, fileNamepar exemple:

let formData = new FormData()
formData.append('input', input.files[0], input.files[0].name)

et il suffit de le passer dans la demande AJAX. Par exemple:

    let formData = new FormData()
    var d = $('#fileid')[0].files[0]

    formData.append('fileid', d);
    formData.append('inputname', value);

    $.ajax({
        url: '/yourroute',
        method: 'POST',
        contentType: false,
        processData: false,
        data: formData,
        success: function(res){
            console.log('successfully')
        },
        error: function(){
            console.log('error')
        }
    })

Vous pouvez ajouter n nombre de fichiers ou de données avec FormData.

et si vous faites une demande AJAX à partir du fichier Script.js vers le fichier Route dans Node.js, méfiez-vous de l'utilisation
req.bodypour accéder aux données (par exemple, le texte)
req.filespour accéder au fichier (par exemple, l'image, la vidéo, etc.)

kartik tyagi
la source
-1

Dans mon cas, j'ai dû faire une demande POST, qui contenait des informations envoyées via l'en-tête, ainsi qu'un fichier envoyé à l'aide d'un objet FormData.

Je l'ai fait fonctionner en utilisant une combinaison de certaines des réponses ici, donc essentiellement ce qui a fini par fonctionner était d'avoir ces cinq lignes dans ma demande Ajax:

 contentType: "application/octet-stream",
 enctype: 'multipart/form-data',
 contentType: false,
 processData: false,
 data: formData,

Où formData était une variable créée comme ceci:

 var file = document.getElementById('uploadedFile').files[0];
 var form = $('form')[0];
 var formData = new FormData(form);
 formData.append("File", file);
ndarriulat
la source
1
contentType: "application/octet-stream",est activement nocif et la seule raison pour laquelle il ne pose pas de problème est que vous le remplacez deux lignes plus tard.
Quentin
1
enctype: 'multipart/form-data',est inutile. jQuery.ajax ne reconnaît pas ce paramètre.
Quentin
… Le reste de votre réponse ne couvre pas le bit "données" des "données et fichiers" du titre de la question.
Quentin
-2
<form id="form" method="post" action="otherpage.php" enctype="multipart/form-data">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <input name="image" type="file" />
    <button type='button' id='submit_btn'>Submit</button>
</form>

<script>
$(document).on("click", "#submit_btn", function (e) {
    //Prevent Instant Click  
    e.preventDefault();
    // Create an FormData object 
    var formData = $("#form").submit(function (e) {
        return;
    });
    //formData[0] contain form data only 
    // You can directly make object via using form id but it require all ajax operation inside $("form").submit(<!-- Ajax Here   -->)
    var formData = new FormData(formData[0]);
    $.ajax({
        url: $('#form').attr('action'),
        type: 'POST',
        data: formData,
        success: function (response) {
            console.log(response);
        },
        contentType: false,
        processData: false,
        cache: false
    });
    return false;
});
</script>

///// otherpage.php

<?php
    print_r($_FILES);
?>
Shailesh Dwivedi
la source