Caricare dati e file in un unico modulo usando Ajax?


384

Sto usando jQuery e Ajax per i miei moduli per inviare dati e file ma non sono sicuro di come inviare sia dati che file in un unico modulo?

Attualmente faccio quasi lo stesso con entrambi i metodi, ma il modo in cui i dati vengono raccolti in un array è diverso, i dati usano .serialize();ma i file usano= new FormData($(this)[0]);

È possibile combinare entrambi i metodi per poter caricare file e dati in un unico modulo tramite Ajax?

Dati jQuery, Ajax e 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>

File jQuery, Ajax e 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>

Come posso combinare quanto sopra in modo da poter inviare dati e file in un unico modulo tramite Ajax?

Il mio obiettivo è essere in grado di inviare tutto questo modulo in un solo post con Ajax, è possibile?

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

2
L' FormDataapproccio dovrebbe funzionare bene con i moduli che contengono ciò che vuoi, non solo i campi di caricamento dei file; tuttavia non è ampiamente supportato.
lanzz,

@lanzz quale però? quello con serializzazione sembra funzionare solo per i dati ma l'altro sembra funzionare solo per i file?
Dan,

A giudicare da questa pagina MDN , tutti i dati del modulo devono essere inviati quando si utilizzaFormData
lanzz

1
@lanzz hai ragione, funziona come pensavo che dovessi usare l'id del modulo sbagliato, puoi caricare sia i file che i dati tramite un modulo con ajax.
Dan,

Questo sembra non funzionare in presenza di input di file a selezione multipla. Carica solo il primo file.
Sami Al-Subhi,

Risposte:


458

Il problema che ho riscontrato è stato l'utilizzo dell'identificatore jQuery errato.

Puoi caricare dati e file con un solo modulo usando 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
    });
});

Versione breve

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

    $.post($(this).attr("action"), formData, function(data) {
        alert(data);
    });
});

17
nelle versioni di IE <10 questa soluzione non funzionerà, poiché FormData è un oggetto HTML5, non presente in IE 8 o 9.
Xavier Guzman,

34
$(this)[0]è solo un alias di this, quindi new FormData(this)dovrebbe essere sufficiente.
rwwt

9
Non sembra possibile ispezionare l'oggetto FormData, vedere questa domanda (per chiunque si imbatta nella stessa incapacità che ho appena fatto perché l'oggetto era sempre vuoto).
Laura,

28
Per i lettori futuri: le dichiarazioni contentType e processData sono importanti. Vedi questa risposta per maggiori informazioni.
AaronSieb,

5
il async: falsenon sembra necessaria per questo lavoro e provoca il blocco sui telefoni (singolo) filettati browser
Jeremy Daalder

34

un'altra opzione è quella di utilizzare un iframe e impostare l'obiettivo del modulo su di esso.

puoi provare questo (usa 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);
        });
    }
}

funziona bene con tutti i browser, non è necessario serializzare o preparare i dati. un lato negativo è che non è possibile monitorare i progressi.

inoltre, almeno per Chrome, la richiesta non verrà visualizzata nella scheda "xhr" degli strumenti di sviluppo ma in "doc"


1
In effetti non è Ajax, può ancora essere utile per le persone con la stessa domanda.
Roey,

3
Non riesco proprio a credere perché questa risposta ottenga -2, ho finito per usarla perché avevo bisogno del supporto legacy del browser
Sijav,

Questa risposta dovrebbe essere nel thread poiché altre risposte indicano che "i browser più vecchi non funzionano" o "l'hacker iframe potrebbe essere usato" ma non li affronta mai. Bel pezzo di codice, che mostra anche come usare correttamente onload +1
mschr

18

Stavo riscontrando lo stesso problema in ASP.Net MVC con HttpPostedFilebase e invece di utilizzare il modulo su Invia avevo bisogno di utilizzare il pulsante sul clic dove dovevo fare alcune cose e quindi se tutto OK il modulo di invio, ecco come ho fatto a farlo funzionare

$(".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
        });
    }
});

questo popolerà correttamente il tuo modello MVC, per favore assicurati che nel tuo Modello, la Proprietà per HttpPostedFileBase [] abbia lo stesso nome del Nome del controllo di input in html ie

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

public class MyViewModel
{
    public HttpPostedFileBase[] UploadedFiles { get; set; }
}

1
Stai risparmiando tempo :):
Suhail Mumtaz Awan,

Nel mio caso ho dovuto usare:contentType : "application/octet-stream"
Christophe Roussy,

Grazie compagno! Hai risparmiato molto tempo.
Accesso negato

Funziona con Django, bello!
csandreas1,

Grazie compagno! Le 2 righe seguenti hanno funzionato per me. var form = $ ("# Form"); var formData = new FormData (form [0]);
Rajiv Kumar

15

O più breve:

$("form#data").submit(function() {
    var formData = new FormData(this);
    $.post($(this).attr("action"), formData, function() {
        // success    
    });
    return false;
});

quindi, come convalidare un campo dati usando lo stesso script, cioè se si dispone di un campo di testo e di un campo file nel modulo
George

6

Per me, non ha funzionato senza enctype: 'multipart/form-data'campo nella richiesta Ajax. Spero che aiuti qualcuno che è bloccato in un problema simile.

Anche se è enctype stato già impostato nell'attributo form , per qualche motivo, la richiesta Ajax non ha identificato automaticamente la enctypedichiarazione senza esplicita (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>

Come altri menzionati sopra, si prega di prestare particolare attenzione ai campi contentTypee processData.


1
"Per me, non ha funzionato senza enctype: campo 'multipart / form-data' nella richiesta Ajax." - Non può aver avuto alcun effetto. Non è una proprietà riconosciuta da jQuery.ajax. Vedi la documentazione dove enctypenon è menzionato affatto.
Quentin,

Come ho detto prima, ho provato più risposte diverse ma non hanno funzionato. Nella console JS è stato mostrato un errore Ajax che indicava un errore di codifica. Successivamente ho seguito questo tutorial che alla fine ha fatto funzionare il mio codice e l'ho pubblicato qui. Forse, il enctypecampo non è coperto dalla documentazione per un motivo. Non ho controllato il codice sorgente di jQuery, quindi non posso dirlo con certezza.
Adithya Upadhya,

"Forse, il campo enctype non è coperto dalla documentazione per un motivo." - Questa ragione è che jQuery non fa nulla con esso, quindi è una sciocchezza.
Quentin,

Forse potresti contattare Mkyong e conversare con lui. Ho testato nuovamente il mio codice rimuovendo il enctypecampo e non carica più i file (restituisce un errore di tipo di codifica). Non sono sicuro di come funzioni poiché non ho controllato il codice sorgente di jQuery. Ho pubblicato questa risposta con l'intenzione di aiutare gli altri che sono bloccati in un problema simile. Non sto pescando voti qui ... Se hai ulteriori domande / commenti, chattiamo invece di commentare.
Adithya Upadhya,

1

Per me il seguente codice funziona

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

Nel tuo metodo Post azione, passa il parametro come HttpPostedFileBase UploadFile e assicurati che l'input del file abbia lo stesso menzionato nel parametro del metodo Action. Dovrebbe funzionare anche con AJAX Begin form.

Ricorda qui che il tuo modulo AJAX BEGIN non funzionerà qui poiché fai la tua chiamata postale definita nel codice sopra menzionato e puoi fare riferimento al tuo metodo nel codice secondo il Requisito

So di rispondere in ritardo, ma questo è ciò che ha funzionato per me


1

Un modo semplice ma più efficace:
new FormData()è esso stesso come un contenitore (o una borsa). Puoi mettere tutto attr o file in sé. L'unica cosa che devi aggiungere attribute, file, fileNamead esempio:

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

e basta passarlo nella richiesta AJAX. Per esempio:

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

È possibile aggiungere n numero di file o dati con FormData.

e se stai facendo una richiesta AJAX dal file Script.js per instradare il file in Node.js, fai attenzione a utilizzare
req.bodyper accedere ai dati (ad esempio testo)
req.filesper accedere al file (ad esempio immagine, video ecc.)


-1

Nel mio caso ho dovuto fare una richiesta POST, che aveva le informazioni inviate tramite l'intestazione e anche un file inviato usando un oggetto FormData.

L'ho fatto funzionare usando una combinazione di alcune delle risposte qui, quindi sostanzialmente quello che ha funzionato è stato avere queste cinque righe nella mia richiesta Ajax:

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

Dove formData era una variabile creata in questo modo:

 var file = document.getElementById('uploadedFile').files[0];
 var form = $('form')[0];
 var formData = new FormData(form);
 formData.append("File", file);

1
contentType: "application/octet-stream",è attivamente dannoso e l'unica ragione per cui non causa problemi è perché lo sovrascrivi due righe in seguito.
Quentin,

1
enctype: 'multipart/form-data',è inutile. jQuery.ajax non riconosce quel parametro.
Quentin,

... il resto della tua risposta non riesce a coprire il bit di "dati" di "dati e file" dal titolo della domanda.
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);
?>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.