entre Desarrolladores

Recibe ayuda de expertos

Registrate y pregunta

Es gratis y fácil

Recibe respuestas

Respuestas, votos y comentarios

Vota y selecciona respuestas

Recibe puntos, vota y da la solución

Pregunta

3votos

Envió de form con archivos

Con jQuery para enviar formularios sencillos basta con el siguiente código:

$(document).ready(function() {  
                 $("body").on("submit", "form", function( event ) {
            $(this).replaceWith('Pensando...');

            event.preventDefault();             

            var type = "POST";  
            var url = $(this, "form").prop("action");   
            var cadena = $(this, "form").serialize();   
            alert("Cadena: " + cadena);

            $.ajax({
                type: type,
                url: url,
                data: cadena,                           
                success: function(data) {
                    if (data) {
                        //alert(data);
                        $(".section").html(data);
                        return;
                    }                   
                    else {
                        //alert(data);
                        alert("Data no tiene valor");
                    }           
                }
            });
        });
});

Pero cuando el formulario contiene un archivo o imagen, esto no sirve, buscando por Internet encontré el uso de FormData, para el envió de archivos mediante AJAX, lo intento aplicar, pero no funciona:

$(document).ready(function() {  
                $("body").on("submit", "form", function( event ) {
            $(this).replaceWith('Pensando...');

            event.preventDefault();     

            var url = $(this, "form").prop("action");     
            var fd = new FormData($(this, "form"));
            var tipo = "POST";

            alert(JSON.stringify(fd));

            $.ajax({
                url: url,           
                data:   fd, 
                type: tipo, 

                success: function(data) {
                    if (data) {
                        $(".section").html(data);
                        return;
                    }                   
                    else {
                        alert("Data no tiene valor");
                    }           
                }
            });
        }); 
});

El alert que debería mostrar la cadena, aparece vacía.
.section no es más que una clase de <section> en el index, donde se cargar el formulario y donde debería redirigirse el resultado.

bichomen comentado Abr 18

Mirando la consola de Chrome el error que me da es el siguiente:

jquery.min.js:4 Uncaught TypeError: Illegal invocation

y lo indica sobre la linea del ajax ¿Que estoy haciendo mal?

3 Respuestas

2votos

bichomen Puntos2460

Bueno lo consegui, pongo el resultado


        if (window.File && window.FileReader && window.FileList && window.Blob) {
            //Great success! All the File APIs are supported.
        } else {
            alert('Si esta viendo esto por favor dimelo.');
        }

        $("body").on("change", "#files", function(event) {
            var files = event.target.files[0];

            /*if (!files.type.match('image.*')) {
                continue;
        }*/

        var reader = new FileReader();

            reader.onload = (function(theFile) {
            return function(e) {
              $(datos).html('<img class="thumb" src="' + e.target.result + '" title="' + escape(theFile.name) + '"/>');
            };
            })(files);

            reader.readAsDataURL(files);
        });

        $("body").on("submit", "form", function(event) {
                event.stopPropagation();
            event.preventDefault();   

                var type = "POST";  
                var url = $(this, "form").prop("action");       

                if(event.target.files) {    
               var file = event.target.files;   
                var elementos = $(this, "form")[0].elements;

                    var data = new FormData();              

                    for( var i = 0; i < elementos.length; i++ ) {
                        element = elementos[i];
                        data.append(element.name,element.value);
                    }               

                    var file_data = $(file).prop("files")[0];                             
                data.append("files", file_data);                    

                    var cache = false;
                    var contentType = false;
                    var processData = false;
            }
            else {
                var data = $(this, "form").serialize(); 
            }   

            $.ajax({          
                url: url,
               data: data,
               type: type, 
               cache: cache,
                contentType: contentType,
                    processData: processData

            })
            .done(function(data) {                  
                $(".section").html(data);
            }); 
        }); 

Solo un pequeño detalle

if (!files.type.match('image.*')) {
                continue;
        }

Esta parte del texto es para que solo acepte imágenes, pero no funciona, pero bueno es algo secundario que ya puedo mirar con más calma.

Saludos

Peter comentado Abr 25

Gracias por compartir la solución.

2votos

gerko23 Puntos1870

hola @bichomen puede que a tu formulario en la carga en el codigo de AJAX le
falte estas lineas:

cache: false,
contentType: false,
processData: false,

bichomen comentado Abr 19

Ya lo probé y nada

2votos

Leonardo-Tadei Puntos211470

Hola @bichomen,

para enviar un archivo con AJAX, hay que previamente procesar los INPUT de tipo FILE, y también cambiar el tipo de formulario para que sea multipart-form-data.

El HTML es siempre subyacente, así que hay que respetar sus reglas de funcionamiento.

Para procesar el archivo y poderlo cargar como dato:

// Variable para almacenar los archivos
var files;
// Manejador del evento onChange
$('input[type=file]').on('change', prepareUpload);
// Procesaro el archivo para cargarlo a la variable
function prepareUpload(event) {
  files = event.target.files;
}

Luego ajustar el envío para que el tipo de formulario pueda manejar el arvhivo:

// Crea un objeto formdata y agrega los archivos
    var data = new FormData();
    $.each(files, function(key, value) {
        data.append(key, value);
    });

    $.ajax({
        url: 'submit.php?files',
        type: 'POST',
        data: data,
        cache: false, // Para evitar la cache
        dataType: 'json',
        processData: false, // No procesar los archivos
        contentType: false, // multipart-form-data
        success: function(data, textStatus, jqXHR)
        {
        ...

En este enlace tenés un buen tutorial que explica todo paso a paso.

Saludos cordiales!

bichomen comentado Abr 19

Ya había probado esto pero de otra forma aun así he adaptado tu código a mi necesidad, pero sigue sin ir:

var files;

        function prepareUpload(event) {
            files = event.target.files;
        }       

        $('input[type=file]').on('change', prepareUpload);

        $("body").on("submit", "form", function(event) {        
            event.stopPropagation();
            event.preventDefault(); 

            var f = $(this, "form");
            var url = f.prop("action");

            var data = new FormData(this, "form");
            $.each(files, function(key, value) {
                data.append(key, value);
            });

             alert(JSON.stringify(data));

            $.ajax({
            url: url,
            type: 'POST',
            data: data,
            cache: false,
            dataType: 'json',
            processData: false,
            contentType: false, 
            success: function(data) {
                    if (data) {
                        alert(data);
                        $(".section").html(data);
                        return;
                    }                   
                    else {
                        alert("Data no tiene valor");
                    }           
                }
            });
        });

Si utilizo $('form').on('submit', funciona, si, pero carga modificar.php (La página que contiene el formulario y lo procesa) a página completa y yo quiero que lo cargue dentro de la sección "<section>" para eso utilizo $("body").on("submit", "form", pero por alguna razón que desconozco, me devuelve el data vació.

Tengo que decir que en formulario no envió solo archivos, sino campos varios y puede o no subirse archivos.

bichomen comentado Abr 19

He montado en CodePen el código para que se vea en acción y así se entienda mejor, es un poco rustico, porque codePen no admite php, pero creo que se entiende lo que quiero lograr:

https://codepen.io/bichomen/project/editor/AJMJkX/

Leonardo-Tadei comentado Abr 20

O ahora no te entiendo, o eso no va a funcionar... el problema que tenés es que el formulario se envía en vez de ser capturado por el evento que hace la llamada AJAX, pero aun así, en caso de hacerse la llamada, el DIV de clase "section" en dónde querés mostrar el resultado está en otro archivo, fuera del DOM del documento que estás manejando.

O el HTML que pusiste no es realmente así, o es imposible que modificar.html pueda cambiar un DIV de index.html !

Es muy posible que usar $('form').on('submit'... te funcione, pero que te estés olvidando de recorrer los datos del formulario para cargarlos y enviarlos. Al menos en tu código no se ve la parte del manual en el enlace que te envié que hace

function submitForm(event, data){
  // Create a jQuery object from the form
    $form = $(event.target);

    // Serialize the form data
    var formData = $form.serialize();
...

Es como que estás agregando los FILE, pero te estás olvidando de agregar todo el resto d ela información al FormData. Recordá que el FormData empieza vacío al momento de craerlo, y que enviará lo que sea que le cargues, pero nada si no le cargas nada, independientemente de que esté en el FORM HTML que estás procesando.

Saludos!

bichomen comentado Abr 20

Varias cosas

Yo creía que poniendo un ejemplo practico se iba a entender lo que quiero conseguir pero veo que no.

Yo tengo una página donde dentro de un div se carga un formulario con varios campos textarea, text,checkbox,select, etc y entre ellos en uno para subir archivos, como puse arriba del todo, con serialize() puedo enviar formularios sin problemas, pero cuando tengo un formulario como este donde se puede enviar un archivo este da un error y no envía o carga la respuesta, con PHP que es lo que no he podido implementar en el ejemplo que he puesto, recupero por post los campos subidos y los muestro, esta es la intención.

Después de mirar varios ejemplos, porque al principio no lo entendía, se ve que por data se pasa el archivo, pero es que también por data se envían los campos por post, osea o una cosa o la otra, para poder enviar tanto los campos como el archivo, hay que hacer una cadena concatenando los datos y pasarla al data.

bichomen comentado Abr 20

El código debería ser algo así, por data se pasan todos los datos más el archivo subido, si lo hay:


        $("body").on("submit", "form", function(event) {
                event.stopPropagation();
            event.preventDefault();   

                var f = $(this, "form");
                var url = f.prop("action");

                $form = $(event.target);                
                var form = $form.serialize();               

                var files = event.target.files;                 
                var file = new FormData();

                $.each(files, function(key, value)
                {
                    file.append(key, value);
                });

                alert("Datos: " + JSON.stringify(form));                
            alert("Archivo: " + JSON.stringify(file));

            $.ajax({          
               url: url,
               type: "post",              
               data: form + file,
               cache: false,
                contentType: false,
                    processData: false                  
            })
            .done(function(data) {
                alert(data);
               $(".section").html(data);
            }); 
        }); 

Leonardo-Tadei comentado Abr 21

Hola @bichomen,

con el código que subiste se entiende la idea, pero no queda clara la implementación: es cómo porciones de código sueltas en 3 archivos, pero que tendrían que funcionar como un solo archivo.

El objeto FormData es perfectamente capaz de enviar datos por POST a la vez que archivos: no te hagas problema por esto.

Respecto a tu código de más arriba, tiene varias incongruencias, como si estuvieras mitad escribiendo y mitad copiando y pegando sin adaptar al resto de tu script:

Por ejemplo, si var f = $(this, "form"); hace que f sea el formulario a tratar, para qué lo volvés a llamar con otro nombre en $form = $(event.target);? Tanto f como $form tienen el mismo contenido!

Luego, en vez de tener todo cargado en un solo objeto de tipo FormData, estás serializando una parte y concatenando el string resultante al FormData, lo que no va a funcionar porque un string y un FormData son cosas distintas...

Te paso una porción de código que agrega todos los elementos d eun formulario a un FormData:

...
form = document.getElementById("id_del_form");
var fd = new FormData();
for( var i = 0; i < length; i++ ) {
    element = form.elements[i]
    fd.append(element.name,element.value);
}
...

Si a este FD le agregás los file con el bucle que ya tenés, al final solo tenés que enviar solo esto:

...
$.ajax({          
    url: url,
    type: "post",              
    data: fd,
    cache: false,
    contentType: false,
    processData: false                  
})
...

El único ingrediente que falta es que la salida del script PHP tenga HTML válido para que se vea correctamente dentro del DIV de clase "section".

Saludos cordiales!

bichomen comentado Abr 24

Creo que estoy muy cerca, pero sigue habiendo algo que se me escapa.

He modificado el código, añadí más funcionalidades:

  • Ahora al elegir la imagen a subir aparece una previsulaización de ella,
  • Al enviar el formulario se carga donde se debe cargar con todos los elementos
  • El código es universal, sirve para cualquier tipo de formulario, no para un formulario especifico.

Peroooo el archivo sigue sin subir...

files.name me devuelve el valor de name del input type="files" ---> "files"
files.value me devuelve en Chrome: "C:\fakepath\imagen.jpg" y en Firefox: "imagen.jpg"

HTML

        <form id="modi" class="form" name="modi" method="post" action="modificar.php" enctype="multipart/form-data" accept-charset="ISO-8859-1">
    <input type="file" id="files" name="files">
                <output id="datos"></output>
</form>

jQuery

$(document).ready(function() {  
    // Check for the various File API support.
        if (window.File && window.FileReader && window.FileList && window.Blob) {
            //Great success! All the File APIs are supported.
        } else {
            alert('Si esta viendo esto por favor dimelo.');
        }

        $("body").on("change", "#files", function(event) {
            var files = event.target.files[0];

            /*if (!files.type.match('image.*')) {
                continue;
        }*/

        var reader = new FileReader();

            reader.onload = (function(theFile) {
            return function(e) {
                //var tam = Math.round((theFile.size / 1024),2);

              /*    $(datos).html('<img class="thumb" src="' + e.target.result + '" title="' + escape(theFile.name) + '"><br> <strong>' + escape(theFile.name) + 
                '</strong> (' + theFile.type + ') - ' + tam + ' Kbs, Ultima modificación: ' + theFile.lastModifiedDate.toLocaleDateString() + '<br>'); */
              $(datos).html('<img class="thumb" src="' + e.target.result + '" title="' + escape(theFile.name) + '">');
            };
            })(files);

            reader.readAsDataURL(files);
        });

        $("body").on("submit", "form", function(event) {
                event.stopPropagation();
            event.preventDefault();   

                var type = "POST";  
                var url = $(this, "form").prop("action");       

                if(event.target.files) {    
               var files = event.target.files;  
                var elementos = $(this, "form")[0].elements;

                    var data = new FormData();              

                    for( var i = 0; i < elementos.length; i++ ) {
                        element = elementos[i];
                        data.append(element.name,element.value);
                    }
                    alert("Nombre: " + files.name);
                    alert("Valor: " + files.value);

                    data.append(files.name, files.value);               
                    /*$.each(files, function(key, value) {
                        data.append(key, value);
                    });*/

                    var cache = false;
                    var contentType = false;
                    var processData = false;
            }
            else {
                var data = $(this, "form").serialize(); 
            }   

            $.ajax({          
                url: url,
               data: data,
               type: type, 
               cache: cache,
                contentType: contentType,
                    processData: processData

            })
            .done(function(data) {
                alert(JSON.stringify(data));    
                $(".section").html(data);
            }); 
        }); 
});

Empiezo a pensar que el problema no es tanto de javascript como si de PHP, ya que nunca entra en esta parte del código:

PHP

if(is_uploaded_file($_FILES['files']['tmp_name'])) {
        $alert = "Nombre de la foto: ".$_FILES['files']['tmp_name'];
        echo '<script language="javascript">alert("'.$alert.'");</script>';
        $uploaddir = 'fotos/';
        $uploadfile = $uploaddir.basename($_FILES['files']['name']);

        if(move_uploaded_file($_FILES['files']['tmp_name'], $uploadfile)) {
            $nombre_archivo = $_FILES['files']['name'];
            $nombre_temporal = $_FILES['files']['tmp_name'];
            $tipo_archivo = $_FILES['files']['type'];
            $tamano_arch = $_FILES['files']['size'];
        }
    }           

Por favor, accede o regístrate para responder a esta pregunta.

¿Conoces alguien que puede responder?
¡Comparte esta pregunta!


Actividad Reciente

¿Eres Usuario Apple?

...

Bienvenido a entre Desarrolladores, donde puedes realizar preguntas y recibir respuestas de otros miembros de la comunidad.

Conecta