¿Carga tanto datos como archivos en una forma usando Ajax?

384

Estoy usando jQuery y Ajax para mis formularios para enviar datos y archivos, pero no estoy seguro de cómo enviar datos y archivos en un formulario.

Actualmente hago casi lo mismo con ambos métodos, pero la forma en que se recopilan los datos en una matriz es diferente, los datos se usan .serialize();pero los archivos usan= new FormData($(this)[0]);

¿Es posible combinar ambos métodos para poder cargar archivos y datos de una forma a través de Ajax?

Datos jQuery, Ajax y 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>

Archivos jQuery, Ajax y 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>

¿Cómo puedo combinar lo anterior para poder enviar datos y archivos de una forma a través de Ajax?

Mi objetivo es poder enviar todo este formulario en una publicación con Ajax, ¿es posible?

<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
fuente
2
El FormDataenfoque debería funcionar bien con formularios que contienen lo que desee, no solo los campos de carga de archivos; Sin embargo, no es ampliamente compatible.
lanzz
@lanzz que sin embargo? el que tiene serializar parece funcionar solo para datos pero el otro solo funciona para archivos.
Dan
A juzgar por esta página MDN , todos los datos del formulario deben presentarse cuando se utilizaFormData
lanzz
1
@lanzz tienes razón, funciona como pensé que debería ser, estaba usando una identificación de formulario incorrecta, puedes subir archivos y datos a través de un formulario con ajax.
Dan
Esto parece no funcionar cuando hay una entrada de archivos de selección múltiple. Solo carga el primer archivo.
Sami Al-Subhi

Respuestas:

458

El problema que tuve fue usar el identificador de jQuery incorrecto.

Puede cargar datos y archivos con un formulario 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
    });
});

Version corta

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

    $.post($(this).attr("action"), formData, function(data) {
        alert(data);
    });
});
Dan
fuente
17
en versiones de IE <10, esta solución no funcionará, ya que FormData es un objeto HTML5, no presente en IE 8 o 9.
Xavier Guzman
34
$(this)[0]es solo un alias de this, por lo que new FormData(this)debería ser suficiente.
r3wt
99
No parece posible inspeccionar el objeto FormData, vea esta pregunta (para cualquiera que se encuentre con la misma falta de idea que acabo de hacer porque el objeto siempre estaba vacío).
Laura
28
Para futuros lectores: las declaraciones contentType y processData son importantes. Vea esta respuesta para más información.
AaronSieb
55
la async: falseno parece necesaria para que esto funcione y causa el bloqueo de los navegadores (sola) roscados móviles
Jeremy Daalder
34

Otra opción es utilizar un iframe y establecer el objetivo del formulario.

puedes probar esto (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 bien con todos los navegadores, no necesita serializar o preparar los datos. Una desventaja es que no puedes monitorear el progreso.

Además, al menos para Chrome, la solicitud no aparecerá en la pestaña "xhr" de las herramientas del desarrollador, sino en "doc"

Roey
fuente
1
De hecho, no es Ajax, aún puede ser útil para las personas con la misma pregunta.
Roey
3
No puedo creer por qué esta respuesta obtuvo -2, terminé usando esto ya que necesitaba soporte de navegador heredado
Sijav
Esta respuesta debe estar en el hilo, ya que otras respuestas indican que 'los navegadores más antiguos no funcionan' o 'podría usarse el pirateo de iframe', pero nunca los aborda. Bonito fragmento de código, que también muestra cómo usar onload correctamente +1
mschr
18

Estaba teniendo este mismo problema en ASP.Net MVC con HttpPostedFilebase y, en lugar de usar el formulario en Enviar, necesitaba usar el botón al hacer clic donde necesitaba hacer algunas cosas y luego, si todo está bien, envíe el formulario, así es como lo hice 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
        });
    }
});

esto rellenará correctamente su modelo MVC, asegúrese de que en su Modelo, La propiedad para HttpPostedFileBase [] tenga el mismo nombre que el Nombre del control de entrada en html, es decir

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

public class MyViewModel
{
    public HttpPostedFileBase[] UploadedFiles { get; set; }
}
h_power11
fuente
1
Usted ahorra tiempo. :)
Suhail Mumtaz Awan
En mi caso tuve que usar:contentType : "application/octet-stream"
Christophe Roussy
¡Gracias amigo! Ahorraste mucho tiempo.
Acceso denegado
Funciona con Django, ¡bien!
csandreas1
¡Gracias amigo! Las siguientes 2 líneas funcionaron para mí. var form = $ ("# Form"); var formData = nuevo FormData (formulario [0]);
Rajiv Kumar
15

O más corto:

$("form#data").submit(function() {
    var formData = new FormData(this);
    $.post($(this).attr("action"), formData, function() {
        // success    
    });
    return false;
});
schaenk
fuente
Entonces, con esto, ¿cómo se valida un campo de datos utilizando el mismo script, es decir, si tiene un campo de texto y un campo de archivo en su formulario
George
6

Para mí, no funcionó sin enctype: 'multipart/form-data'campo en la solicitud de Ajax. Espero que ayude a alguien que está atrapado en un problema similar.

Aunque enctype ya estaba configurado en el atributo de formulario , por alguna razón, la solicitud de Ajax no identificó automáticamente la enctypedeclaración sin 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 otros mencionados anteriormente, preste especial atención a los campos contentTypey processData.

Adithya Upadhya
fuente
1
"Para mí, no funcionó sin enctype: campo 'multipart / form-data' en la solicitud de Ajax". - Eso no puede haber tenido ningún efecto. No es una propiedad reconocida por jQuery.ajax. Consulte la documentación donde enctypeno se menciona en absoluto.
Quentin
Como mencioné antes, probé varias respuestas diferentes pero no funcionaron. Un error de Ajax que indica un error de codificación se mostró en la consola JS. Más tarde seguí este tutorial que finalmente hizo que mi código funcionara y lo publiqué aquí. Quizás, el enctypecampo no está cubierto en la documentación por una razón. No he verificado el código fuente de jQuery, así que no puedo decir con certeza.
Adithya Upadhya
"Quizás, el campo enctype no está cubierto en la documentación por una razón". - Esa razón es que jQuery no hace nada con él, así que no tiene sentido.
Quentin
Quizás podrías contactar a Mkyong y tener una conversación con él. Probé mi código nuevamente al eliminar el enctypecampo y ya no carga archivos (devuelve el error de tipo de codificación). No estoy seguro de cómo funciona, ya que no he comprobado el código fuente de jQuery. Publiqué esta respuesta con la intención de ayudar a otros que están atrapados en un problema similar. No estoy buscando votos a favor aquí ... Si tiene más preguntas / comentarios, hablemos en lugar de comentar.
Adithya Upadhya
1

Para mí siguiendo el código de trabajo

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

En su Método de publicación de acciones, pase el parámetro como HttpPostedFileBase UploadFile y asegúrese de que la entrada de su archivo tenga la misma que se menciona en su parámetro del Método de acción. También debería funcionar con el formulario AJAX Begin.

Recuerde aquí que su Formulario AJAX BEGIN no funcionará aquí ya que realiza su llamada posterior definida en el código mencionado anteriormente y puede hacer referencia a su método en el código según el Requisito

Sé que estoy respondiendo tarde, pero esto es lo que funcionó para mí.

Pranav Kulshrestha
fuente
1

Una manera simple pero más efectiva:
new FormData()es en sí misma como un contenedor (o una bolsa). Puede poner todo el atributo o archivo en sí mismo. Lo único que necesitará para agregar, attribute, file, fileNamepor ejemplo:

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

y solo pásalo en la solicitud AJAX. P.ej:

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

Puede agregar n número de archivos o datos con FormData.

y si está haciendo una solicitud AJAX desde el archivo Script.js al archivo de ruta en Node.js, tenga cuidado de usar
req.bodypara acceder a los datos (es decir, texto)
req.filespara acceder al archivo (es decir, imagen, video, etc.)

kartik tyagi
fuente
-1

En mi caso, tuve que hacer una solicitud POST, que tenía información enviada a través del encabezado, y también un archivo enviado usando un objeto FormData.

Lo hice funcionar usando una combinación de algunas de las respuestas aquí, así que básicamente lo que terminó funcionando fue tener estas cinco líneas en mi solicitud de Ajax:

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

Donde formData era una variable creada así:

 var file = document.getElementById('uploadedFile').files[0];
 var form = $('form')[0];
 var formData = new FormData(form);
 formData.append("File", file);
ndarriulat
fuente
1
contentType: "application/octet-stream",es activamente dañino y la única razón por la que no causa un problema es porque lo sobrescribe dos líneas más tarde.
Quentin
1
enctype: 'multipart/form-data',no tiene sentido jQuery.ajax no reconoce ese parámetro.
Quentin
... el resto de su respuesta no cubre el bit de "datos" de "datos y archivos" del título de la pregunta.
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
fuente