Subir archivos usando Html5-XMLHttpRequest
http://www.developphp.com/video/JavaScript/File-Upload-Progress-Bar-Meter-Tutorial-Ajax-PHP
onreadystatechange::Responde a la solicitud realizada por el objeto http
Trabajar con ficheros en JavaScript consiste principalmente de:
- Subir ficheros
- Previsualizar ficheros en el DOM y
- Descargar ficheros
Apuntar directamente al formulario: esto serializará todos los inputs del formulario como «application/x-www-form-urlencoded» por defecto o bien como el tipo indicado en el atributo «enctype» del formulario.
- var formData = new FormData(document.getElementById('file_upload_form'));
Recoger los datos manualmente: para esto habrá que crear un objeto FormData vacío e ir metiéndole los datos con el método «append(<nombre_input>, <valor_input>)«. Nótese que para los inputs que no sean ficheros hay que añadirle el atributo «value» de dicho input; sin embargo, para los ficheros, hay que añadirle el atributo «files[i]» del input. El atributo «files» es un array que contiene los metadatos de cada uno de los ficheros que se han seleccionado en el input de tipo file. Podemos mandar todos los ficheros en una sola petición HTTP o bien mandar un fichero por cada petición HTTP (normalmente se mandan todos los ficheros juntos cuando la lógica de la operación requiere todos los ficheros a la vez, sino, lo ideal es mandarlos por separado ya que, en caso de fallo, el rollback de la grabación del fichero es mucho más sencillo).
IMPORTANTE: El formulario por defecto provocará que se navegue hacia la página donde se procesa la petición HTTP. Para evitar esto y conseguir que solamente se mande asíncronamente (mediante AJAX) hay que prevenir el evento por defecto («event.preventDefault()«).
- documnent.getElementById('file_upload_form').onsubmit = uploadFormOnSubmit;
- function uploadFormOnSubmit(event){
- // Cancelamos la operación por defecto
- event.preventDefault();
- // Crear el objeto FormData y mandarlo por AJAX
- }
Progreso de subida
Cuando subimos ficheros, es habitual querer saber qué porcentaje se ha subido en cada momento.
El objeto XMLHttpRequest tiene un atributo llamado «upload«, que no es más que un objeto que devuelve información del estado de la subida de los datos mandados a través de AJAX.
Entonces, para mantener una barra de progreso de la subida de los datos que queremos mandar al servidor, se deben añadir los siguientes «EventListeners=Escucha de eventos cuando load,error.porgress» al objeto «upload«:
- progress: periódicamente se lanza este evento que indica la cantidad total de bytes que se ha transmitido al servidor.
- load: cuando termina la subida de los datos pero antes de que se devuelva la respuesta de la petición HTTP.
- error: cuando la subida ha fallado (motivo desconocido).
- timeout: cuando la subida ha tardado tanto que se ha devuelto un estado de timeout.
- xhttp.upload.addEventListener("progress", function(){...});
- xhttp.upload.addEventListener("load", function(){...});
- xhttp.upload.addEventListener("error", function(){...});
- xhttp.upload.addEventListener("timeout", function(){...});
Para mantener actualizada una barra de progreso, en las funciones de cada evento, se debe manipular un nodo HTML de tipo «progress» de forma adecuada.
Se pueden usar otras barras de progreso no estándar creadas mediante CSS, pero, para simplificarlo, vamos a usar la etiqueta «progress«.
Por ejemplo:
- xhttp.upload.addEventListener("progress", function(){
- // event.loaded indica cuánto se ha subido al servidor
- // event.total indica cuánto se está intentando enviar
- var pc = parseInt((event.loaded / event.total * 100));
- // progressBar = nodo <progress> de HTML
- progressBar.value = pc;
- // progressPercentaje = nodo de texto donde poner el porcentaje
- progressPercentaje.innerHTML = pc + '%';
- });
Cuando se reciba el evento «load«, deberíamos crear un loader infinito, esto es, un indicador de progreso cuyo tiempo y porcentaje de ejecución es indeterminado, ya que en este momento se está procesando la petición en el servidor y no podemos saber cuánto va a tardar en terminar.
Por ejemplo:
- // Si usamos font awesome tenemos un spinner loader
- // a modo de icono, fácil de meter en una misma línea.
- // En la misma línea donde ponía el porcentaje del progreso
- // podríamos poner algo como esto:
- // CSS:
- // Importar CSS de font awesome
- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
- // JS:
- // Los datos están en el servidor pero aún no se ha recibido
- // respuesta HTTP
- xhttp.upload.addEventListener("load", function(){
- progressPercentaje.innerHTML = '<i class="fa fa-spinner fa-pulse"></i> Guardando...';
- });
Y finalmente, cuando la petición HTTP nos devuelva una respuesta, deberíamos indicar que el fichero (o ficheros, si los mandamos todos en una sola petición HTTP) ya se ha subido.
PREVISUALIZAR FICHEROS
Para ver los ficheros en el DOM del documento HTML, se deben crear los nodos adecuados apuntando a la URL del archivo (o del script del servidor que nos dé el fichero).
Es importante saber el tipo de fichero que se va a emplear para saber cómo tratarlo, pero esencialmente hay 3 tipos de ficheros: vídeo, audio e imagen:
- <!-- vídeo -->
- <video controls>
- <source src="URL_AL_FICHERO" type="MIME">
- </video>
- <!-- audio -->
- <audio controls>
- <source src="URL_AL_FICHERO" type="MIME">
- </audio>
- <!-- imagen -->
- <img src="URL_AL_FICHERO">
Donde pone URL_AL_FICHERO puede ser un acceso directo al fichero (por ejemplo: www.example.com/media/nombre_fichero.jpg) o bien puede ser un script del servidor que nos devolverá el fichero si estamos autorizados a visualizarlo (por ejemplo:
www.example.com / ajax / files / download.php ? file_name = nombre_fichero.jpg).
Donde pone MIME debemos indicar el tipo de objeto MIME que tiene el fichero (debería proporcionarlo el servidor en lugar de tener que calcularlo cada vez que se quiera visualizar el fichero).
Entonces, en JavaScript, crear la vista es tan sencillo como:
- function getFile(mime_type, file_name){
- function newNode(node){return document.createElement(node);}
- switch(mime_type){
- case 'video/mp4':
- case 'video/mkv':
- var video = this.newNode('video');
- video.setAttribute('controls', '');
- var source = this.newNode('source');
- source.setAttribute('src', file_name);
- source.setAttribute('type', mime_type);
- video.appendChild(source);
- return video;
- break;
- case 'audio/mpeg':
- case 'audio/wav':
- var audio = this.newNode('audio');
- audio.setAttribute('controls', '');
- var source = this.newNode('source');
- source.setAttribute('src', file_name);
- source.setAttribute('type', mime_type);
- audio.appendChild(source);
- return audio;
- break;
- case 'image/jpeg':
- case 'image/gif':
- case 'image/bmp':
- case 'image/png':
- var img = this.newNode('img');
- img.setAttribute('src', file_name);
- return img;
- break;
- default:
- console.warn('Tipo de fichero no controlado: '+mime_type);
- return null;
- break;
- }
- }
El objeto XMLHttpRequest
El objeto XMLHttpRequest ha sido actualizado varias veces desde que fue definido como parte del esfuerzo WHATWG’s HTML utilizando la tecnología de Microsoft; entonces tuvimos la especificación original XMLHttpRequest Level 1 como parte de la W3C, también tuvimos la especificación actualizada XMLHttpRequest Level 2, y ahora tenemos la últma version de este documento conocido como XMLHttpRequest Living Specification. Podemo resumir sus ventajas en los siguientes puntos:
- Permite subir y bajar archivos como flujo de bytes (stream bytes), archivos binarios de gran tamaño (BLOBs) o formularios de datos.
- Tiene manejadores de eventos de progreso, errores, aborto, comienzo, y fin de operaciones.
- Peticiones inter dominio (cross-domain or CORS)
- Nuevos tipos de respuestas para JSON
- Es parte fundamental de la HTML5 File API specification
El código anterior se explica casi por si solo, pero resumamos lo que tiene:
- Un input de tipo file que servirá para seleccionar el archivo que se desa subir.
- Un div que servirá para imprimir el tamaño del archivo seleccionado.
- Un div que servirá para imprimir el tipo MIME del archivo seleccionado.
- Un botón que disparará el proceso para subir el archivo elegido.
- Una barra que indicará el progreso en el proceso de subida del archivo (nuevo elemento HTML5).
- Finalmente un div donde se mostrará el progreso en formato de porcentaje.
La función selectedFile()
Cada vez que selecciona un archivo con el elemento file, también dispara al evento onchange el cual llama a la función selectedFile(). En esta función suceden cosas interesantes, para empezar se hace referencia a una colección de archivos instanciada por un objeto nuevo en HTML5 llamado FileList, los objetos que obtenemos como miembros de las lista de FilesList son objetos File. En este caso obtendremos las propiedades size y type desde el objeto File recuperado.
Aprovenchando la información que proporciona la propiedad size, dentro de la función se calcula y se muestra en megabytes o kilobytes el tamaño del archivo que se ha seleccionado. Con la propiedad type se obtiene el tipo MIME del archivo seleccionado, el cual se muestra en el div correspondiente.
function FIEL_fileSelected(Obj) {
FIEL_RestVal(); var Arch1 = document.getElementById("ArchPDF").value.toUpperCase(); var Ext1 = Arch1.substr(Arch1.length-4, 4); if (Ext1.length>0){ if (Ext1!==".PDF"){ alert("El archivo seleccionado no es válido"); document.getElementById("ArchPDF").value = ""; return false; } }}
La función uploadFile()
Esta es la función que hace un mayor uso de las nuevas posibilidades de XMLHttpRequest , y es la que se encargará de disparar el proceso principal del lado cliente.
function FIEL_startUploading() {
// get form data for POSTing---obtener datos de formulario para POSTING var vFD = new FormData(document.getElementById('FIEL_upload_form'));
// create XMLHttpRequest object, adding few event listeners, and POSTing our data //crear un objeto XMLHttpRequest, agregar pocos detectores de eventos y publicar nuestros datos var oXHR = new XMLHttpRequest();
//Evento que se dispara cada vez que hay un avance en el proceso que sube el archivo. oXHR.upload.addEventListener('progress', FIEL_uploadProgress, false);
// Evento que se dispara cuando la transferecia se completa. oXHR.addEventListener('load', FIEL_uploadFinish, false); oXHR.addEventListener('error', FIEL_uploadError, false); oXHR.addEventListener('abort', FIEL_uploadAbort, false); oXHR.open('POST', 'UpLoad_PDF.php'); oXHR.send(vFD);}
Inicialmente tenemos una variable url que usaremos para indicar donde está la página o servicio web que recibirá la petición de esta página para hacer el proceso indicado en el servidor; enseguida tal y como se hizo en la funcion selectedFile() se hace referencia al objeto File miembro FileList obtenido.
En la cuarta línea hay algo novedoso y muy útil, me refiero al objeto FormData, este objeto permite instanciar vía JavaScript un formulario web, es decir, es como si usted colocara con etiquetas HTML un formulario, o bien puede hacer referencia a uno ya existente asignándolo a un objeto FormData. Sin duada esto es de gran ayuda ya que significa que ahora usted puede crear un formulario y alterar los valores que envía de manera dinámica. Para adjuntar valores a un formualrio instanciado o referenciado con FormData se utiliza el método append(archivo, objeto), de esta manera en la quinta línea se agrega nuestro objeto File con el nombre de archivo.
Este es el fragmento de la función que abarca lo planteado:
Manejadores de eventos
Continuando con el resto de la función, podemos observar que finalmente instancía el objeto XMLHttpRequest y se asigna a la variable xmlHTTP, enseguida procedemos a la siguiente novedad, me refiero a la posibilidad de crear los nuevos eventos que forman parte de XMLHttpRequest Level 2 gracias al objeto upload. Los eventes que se agregan en este caso son:- loadstart. Evento que se dispara cuando inicia el proceso para subir el archivo.
- progress. Evento que se dispara cada vez que hay un avance en el proceso que sube el archivo.
- load. Evento que se dispara cuando la transferecia se completa.
- error. Se dispara si el proceso falla con error explícito.
- abort. Se dispara si el usuario interrumpe o la conexión de interrumpe.
No son los único eventos disponibles, consulte la especificación oficial para mayor información.
Los manejadores de eventos se declaran en el siguiente código:
Las funciones que se llaman en cada evento son las siguientes:
La función progressFunction es la que actualiza tanto la barra de estado como el porcentaje que indican de manera gráfica y numérica el avance del proceso, el resto de las funciones únicamente despliegan un mensaje apropiado para el caso.
Código comentado
Si ha observado el código presentado habrá notado algunas líneas comentadas, eso es debido a que este es el código base para hacer algo un poco más complejo, pero decidí dejar esas líneas porque pueden ser una referencia útil para alguien:
La línea anterior es una llamada a un servicio HTTP .Net en lugar de a una página.
Esta línea llama a una función que muestra un mensaje cuando inicia el proceso, la cual comenté porque después de ejecuar varias veces el código me pareció molesto.
El código completo
Así luce la implementación completa del código, no describo el código CSS3 ya que es irrelevante en lo que respecta a la funcionalidad, pero comparto una imágen que muestra como se ve ejecutándose:
No descibo el CSS3 porque es irrelevante en términos de funcionalidad, pero comparto una imagen que muestra como luce la implementación en el navegador y un enlace al CSS3 estilosUploadFile.zip.
También comparto el HTTP service que utilicé para este ejemplo (el código de lado servidor, archivo de backend o cualquier otro nombre que ud. prefiera ) pero esto no será de mucha ayuda amenos que utilice exactemente el mismo stack que yo utilicé en su momento. En otras palabras, si usted es el tipo de personas que solo quiera copiar y pegar….jejeje bueno…quizá no está listo para hace esto todavía. Aquí está el tan solicitado archivo WSUploadFile.zip
Es todo por ahora amigos, espero que esto les sea de utilidad.
Algunos buenos libros que pueden ayudarle en su viaje por HTML5:
Subir archivos usando Html5
XMLHttpRequest tiene nuevos cambios en la especificación Html5, que especifica que la especificación XMLHttpRequest Nivel 2 (la última versión) contiene las siguientes características nuevas:
Maneje secuencias de bytes, como archivos, blobs y objetos FormData para cargar o descargar eventos de progreso durante la carga o descarga Solicitudes entre sitios Permitir la creación de solicitudes anónimas Puede establecer tiempos de espera de solicitudes
https://www.youtube.com/watch?v=1kA4DowJs08&t=49s
https://www.youtube.com/watch?v=TF-fwphqt7g
#2 AJAX con JavaScrip
https://www.youtube.com/watch?v=M4LaQ3KUGOM&list=PLPl81lqbj-4L6JypMCzyMqZ0v87dltjqD&index=3
#3 AJAX con JavaScript
https://www.youtube.com/watch?v=M3w4S94Vl7Q&list=PLPl81lqbj-4L6JypMCzyMqZ0v87dltjqD&index=4
Crear el objeto XMLHttpRequest
https://www.youtube.com/watch?v=TF-fwphqt7g
AJAX FÁCIL y SIN FRAMEWORKS [ 01 de 05
https://www.youtube.com/watch?v=TF-fwphqt7g
https://www.youtube.com/watch?v=GXMNfstLBOI
AJAX FÁCIL y SIN FRAMEWORKS [ 02 de 05 ]
AJAX FÁCIL y SIN FRAMEWORKS [ 03 de 05
Qué es XMLHttpRequest en AJAX y como funciona
Estados y Propiedades en XMLHttpRequest
AJAX FÁCIL y SIN FRAMEWORKS [ 04 de 05 ] -archivo
#1 AJAX con Vanilla JavaScript - XMLHttpRequest |
Enviar datos por método POST a PHP con Formulario HTML/JAVASCRIPT
https://www.youtube.com/watch?v=nLrL9Ip3tWI&t=1s

Comentarios
Publicar un comentario