Carregando dados e arquivos em um formulário usando o Ajax?

384

Estou usando jQuery e Ajax para que meus formulários enviem dados e arquivos, mas não sei como enviar dados e arquivos em um formulário?

Atualmente, faço quase o mesmo com os dois métodos, mas a maneira pela qual os dados são reunidos em uma matriz é diferente, os dados usam, .serialize();mas os arquivos usam= new FormData($(this)[0]);

É possível combinar os dois métodos para poder fazer upload de arquivos e dados em um formulário através do Ajax?

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

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

Como posso combinar o que foi dito acima para enviar dados e arquivos de uma forma via Ajax?

Meu objetivo é poder enviar todo esse formulário em um post com o Ajax, é possível?

<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
fonte
2
A FormDataabordagem deve funcionar bem com formulários que contêm o que você deseja, não apenas os campos de upload de arquivo; embora não seja amplamente suportado.
lanzz
@lanzz que embora? aquele com serializar parece funcionar apenas para dados, mas o outro parece funcionar apenas para arquivos?
Dan
A julgar por esta página MDN , todos os dados do formulário deverá ser apresentado quando você usaFormData
lanzz
11
@lanzz você está certo, funciona como eu pensei que deveria estar. Eu estava usando o ID do formulário errado, você pode fazer upload de arquivos e dados através de um formulário com ajax.
Dan
Isso parece não funcionar quando há entrada de arquivo com seleção múltipla. Carrega apenas o primeiro arquivo.
Sami Al-Subhi

Respostas:

458

O problema que tive foi usar o identificador jQuery errado.

Você pode carregar dados e arquivos com um formulário usando o 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
    });
});

Versão curta

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

    $.post($(this).attr("action"), formData, function(data) {
        alert(data);
    });
});
Dan
fonte
17
em versões do IE <10 esta solução não irá funcionar, como FormData é um objecto HTML 5, não presente no IE 8 ou 9.
Xavier Guzman
34
$(this)[0]é apenas um apelido de this, portanto new FormData(this)deve ser suficiente.
R3wt 12/12
9
Não parece possível inspecionar o Objeto FormData, veja esta pergunta (para qualquer um que esteja na mesma ignorância que eu, porque o Objeto estava sempre vazio).
Laura
28
Para futuros leitores: as declarações contentType e processData são importantes. Veja esta resposta para mais informações.
AaronSieb
5
o async: falsenão parece necessário para que isso trabalho e faz com que o bloqueio em (única rosca) navegadores móveis
Jeremy Daalder
34

outra opção é usar um iframe e definir o destino do formulário para ele.

você pode tentar isso (ele 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);
        });
    }
}

funciona bem com todos os navegadores, você não precisa serializar ou preparar os dados. Uma desvantagem é que você não pode monitorar o progresso.

Além disso, pelo menos para o chrome, a solicitação não aparecerá na guia "xhr" das ferramentas do desenvolvedor, mas em "doc"

Roey
fonte
11
Na verdade, não é o Ajax, ainda pode ser útil para pessoas com a mesma pergunta.
Roey
3
Eu simplesmente não posso acreditar por que essa resposta é -2, acabei usando isso como eu precisava do navegador legado Suporte
Sijav
Esta resposta deve estar no tópico, pois outras respostas apontam para 'navegadores antigos não funcionam' ou 'iframe hack poderia ser usado', mas nunca os abordam. Bom pedaço de código, mostrando também como usar onload corretamente +1
mschr
18

Eu estava tendo esse mesmo problema no ASP.Net MVC com HttpPostedFilebase e, em vez de usar o formulário no envio, precisava usar o botão no clique onde precisava fazer algumas coisas e, se tudo bem, o formulário de envio então aqui está como eu o fiz funcionar

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

isso preencherá corretamente o seu modelo MVC, verifique se o seu Model, The Property for HttpPostedFileBase [] tem o mesmo nome que o Nome do controle de entrada em html, isto é,

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

public class MyViewModel
{
    public HttpPostedFileBase[] UploadedFiles { get; set; }
}
h_power11
fonte
11
Você economiza tempo :):
Suhail Mumtaz Awan /
No meu caso, tive que usar:contentType : "application/octet-stream"
Christophe Roussy
Obrigado parceiro! Você economizou muito tempo.
Acesso negado
Funciona com o Django, legal!
csandreas1
Obrigado companheiro! As duas linhas abaixo funcionaram para mim. var form = $ ("# Form"); var formData = novo FormData (formulário [0]);
Rajiv Kumar
15

Ou mais curto:

$("form#data").submit(function() {
    var formData = new FormData(this);
    $.post($(this).attr("action"), formData, function() {
        // success    
    });
    return false;
});
Schaenk
fonte
Então, com isso, como você validar um campo de dados usando o mesmo script, isto é, se você tem um campo de texto e um campo de arquivo em sua forma
George
6

Para mim, não funcionou sem enctype: 'multipart/form-data'campo na solicitação do Ajax. Espero que ajude alguém que está preso em um problema semelhante.

Embora o enctype atributo já tenha sido definido no formulário , por algum motivo, a solicitação do Ajax não identificou automaticamente a enctypedeclaração sem declaração explícita (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>

Como outros mencionados acima, preste atenção especial aos campos contentTypee processData.

Adithya Upadhya
fonte
11
"Para mim, não funcionou sem o campo enctype: 'multipart / form-data' na solicitação do Ajax." - Isso não pode ter tido nenhum efeito. Não é uma propriedade reconhecida pelo jQuery.ajax. Veja a documentação onde enctypenão é mencionada.
Quentin
Como mencionei antes, tentei várias respostas diferentes, mas elas não funcionaram. Um erro do Ajax indicando erro de codificação foi mostrado no console JS. Posteriormente, segui este tutorial, que finalmente fez meu código funcionar e o publiquei aqui. Talvez, o enctypecampo não esteja coberto na documentação por um motivo. Eu não verifiquei o código fonte do jQuery, portanto não posso dizer com certeza.
Adithya Upadhya
"Talvez, o campo enctype não esteja coberto na documentação por um motivo." - Esse motivo é que o jQuery não faz nada com isso, portanto, é um absurdo.
Quentin
Talvez você possa entrar em contato com Mkyong e conversar com ele. Testei meu código novamente removendo o enctypecampo e ele não carrega mais arquivos (retorna erro de tipo de codificação). Não sei como isso funciona, pois não verifiquei o código-fonte do jQuery. Postei esta resposta com a intenção de ajudar outras pessoas que estão presas em um problema semelhante. Não estou buscando votos positivos aqui ... Se você tiver mais perguntas / comentários, vamos conversar em vez de comentar.
Adithya Upadhya
1

Para mim, seguindo o trabalho de código

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

No seu Action Post Method, passe o parâmetro como HttpPostedFileBase UploadFile e verifique se a entrada do arquivo tem a mesma mencionada no parâmetro do Action Method. Também deve funcionar com o formulário AJAX Begin.

Lembre-se de que aqui o seu formulário AJAX BEGIN não funcionará aqui, pois você faz sua chamada definida no código mencionado acima e pode referenciar seu método no código conforme a exigência

Eu sei que estou respondendo tarde, mas é isso que funcionou para mim

Pranav Kulshrestha
fonte
1

Uma maneira simples, mas mais eficaz:
new FormData()é ela mesma como um recipiente (ou um saco). Você pode colocar tudo attr ou arquivo em si. A única coisa que você precisará acrescentar é, attribute, file, fileNamepor exemplo:

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

e apenas passá-lo na solicitação AJAX. Por exemplo:

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

Você pode acrescentar n número de arquivos ou dados com o FormData.

e se você estiver fazendo uma solicitação AJAX do arquivo Script.js para o arquivo Route no Node.js, tenha cuidado
req.bodyao acessar dados (texto)
req.filespara acessar o arquivo (imagem, vídeo etc.)

kartik tyagi
fonte
-1

No meu caso, eu tive que fazer uma solicitação POST, que tinha informações enviadas através do cabeçalho, e também um arquivo enviado usando um objeto FormData.

Eu fiz funcionar usando uma combinação de algumas das respostas aqui, então basicamente o que acabou funcionando foi ter essas cinco linhas na minha solicitação do Ajax:

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

Onde formData era uma variável criada assim:

 var file = document.getElementById('uploadedFile').files[0];
 var form = $('form')[0];
 var formData = new FormData(form);
 formData.append("File", file);
ndarriulat
fonte
11
contentType: "application/octet-stream",é ativamente prejudicial e o único motivo para não causar um problema é porque você o substitui duas linhas depois.
Quentin
11
enctype: 'multipart/form-data',é inútil. O jQuery.ajax não reconhece esse paramater.
Quentin
… O restante da sua resposta não cobre o bit "data" de "dados e arquivos" do título da pergunta.
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
fonte