Crear Archivos con Javascript – XML e Imagen

Ahora veremos como crear archivos XML y de imagen en el navegador, y usaremos la función descargarArchivo que creamos en el artículo anterior para descargar el archivo.


En el artículo anterior Descargar Archivos con Javascript vimos como se puede generar un archivo de texto y descargarlo directamente desde el navegador.

Ahora veremos como crear archivos XML y de imagen en el navegador, y usaremos la función descargarArchivo que creamos en el artículo anterior para descargar el archivo.

Crear Archivos XML

En principio, un archivo XML es un archivo de texto, así que podríamos crearlo concatenando los datos junto con etiquetas XML de la estructura que queremos generar, y eso está bien si tu estructura es pequeña y simple.

Sin embargo hay dos razones por las cuales tal vez no quieras hacerlo así: Si la estructura es grande o compleja, será difícil hacer seguimiento para determinar cuando abrir o cerrar cada elemento. Además, si el contenido tiene muchos caracteres especiales (no permitidos en XML, como «&» o «<«), debes asegurarte de cambiarlos por entidades XML a medida que concatenas las cadenas.

Pero hay una mejor forma de hacerlo usando el DOM. Primero veamos la estructura del código: Este será el HTML para el ejemplo, muy similar al del artículo anterior:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VT - Crear archivos XML y de imagen con Javascript</title>
    <style>
        .datos-personales{
            display: grid;
            grid-template-columns: 180px 200px;
            row-gap: 1rem;
            margin: 1rem;
            line-height: 1.5rem;
            font-family: Arial, Helvetica, sans-serif;
        }
    </style>
</head>
<body>
    <form class="datos-personales">
        <label for="nombre">Nombre:</label>
        <input id="nombre" type="text" value="Juan Carlos Gonzales"/>
        <label for="fecha">Fecha de Nacimiento:</label>
        <input id="fecha" type="date" value="1982-01-01"/>
        <label for="telefono">Teléfono:</label>
        <input id="telefono" type="tel" value="0800-999.0123"/>
        <label for="direccion">Dirección:</label>
        <textarea id="direccion" rows="4">Av. Principal con calle 18, Urb. La Esmeralda. Edificio Roma & paris, Apto. #12-B</textarea>
        <textarea id="comentario" rows="4">El documento debe contener caracteres <especiales> para mostrar como los convierte en el XML generado.</textarea>
        <button type="button" id="boton-descargar">Descargar Archivo</button>
    </form>
    http://script.js
</body>
</html>

Si vas a seguir el ejemplo, guarda este código con un nombre apropiado (index.html) y ábrelo en el navegador. Como el código es muy simple, no es necesario que lo subas a un servidor para probar. En el navegador verás algo como esto (incluye datos de ejemplo para probar):

Formulario, con campos: nombre, fecha de nacimiento, teléfono, dirección, y comentario
Representación del formulario en el navegador

Antes de continuar hay que explicar lo de los caracteres especiales: un documento XML consiste en un conjunto de elementos delimitados por etiquetas encerradas entre paréntesis angulares «<» y «>»; por esta razón ambos caracteres deben ser reemplazados por las entidades XML «&lt;» y «&gt;», tanto en documentos XML como HTML (como se muestra en el campo comentario del ejemplo). Además, en documentos XML también hay que reemplazar «&» por «&amp;».

Ahora veamos el código para generar el archivo:

document.addEventListener('DOMContentLoaded', (e)=>{

    /**
     * Genera un objeto Blob, que contiene un archivo XML.
     * @returns Blob Objeto de tipo Blob con el archivo xml.
     */
    const generarBlobXml = function(){

        //Comenzamos por crear un XmlDocument
        const documento = document.implementation.createDocument('', '', null);

        //A este, le agregamos un elemento raiz, tal y como
        //cuando manipulamos el DOM:
        const raiz = documento.createElement('datos');
        documento.appendChild(raiz);

        //El elemento raiz debe ser único, pero dentro de él
        //podemos agregar cualquier cantidad de elementos hijos:
        //Usaremos la propiedad textContent para que automáticamente "escape"
        //los caracteres que no pueden incluirse directamente en el XML
        const nombre = documento.createElement('nombre');
        nombre.textContent = document.querySelector('#nombre').value;
        raiz.appendChild(nombre);

        //También puedes agregar atributos a los elementos, 
        //Probemos con uno como ejemplo:
        const fecha = documento.createElement('fecha-nacimiento');
        fecha.setAttribute('formato', 'yyyy-MM-dd');
        fecha.textContent = document.querySelector('#fecha').value;
        raiz.appendChild(fecha);

        //Agregamos los tres últimos elementos
        const telefono = documento.createElement('telefono');
        telefono.textContent = document.querySelector('#telefono').value;
        raiz.appendChild(telefono);

        const direccion = documento.createElement('direccion');
        direccion.textContent = document.querySelector('#direccion').value;
        raiz.appendChild(direccion);

        const comentario = documento.createElement('comentario');
        comentario.textContent = document.querySelector('#comentario').value;
        raiz.appendChild(comentario);

        //Para el blob, necesitamos convertir el documento XML en una cadena
        //de texto. Usamos la propiedad outerHTML del elemento raiz
        const partes = [raiz.outerHTML];

        //El tipo MIME para xml con contenido personalizado como este
        //debe ser "text/xml"
        const blobArchivo = new Blob(partes, { type: 'text/xml'} );

        return blobArchivo;

    }


    /**
     * Inicia la descarga del archivo en el navegador. 
     * Dependiendo de la configuración del navegador, 
     * este descarga el archivo automáticamente, o mostrará
     * el cuadro de dialogo "Guardar Cómo"
     * @param {Blob} archivo Objeto tipo Blob para descargar. 
     * @param {string} nombre Nombre por defecto para el archivo 
     */
    const descargarArchivo = function(archivo, nombre){

        //Creamos un link
        const guardar = document.createElement('a');
        //Le asigna el ObjectURL que "apunta" al Blob
        guardar.href = URL.createObjectURL(archivo);
        //Preferiblemente descargar (con el nombre indicado), 
        //sino, entonces que por lo menos abra en otra ventana
        guardar.download = nombre || 'archivo.dat'; 
        guardar.target = '_blank'; 
        
        //Simulamos un clic del usuario
        //no es necesario agregar el link al DOM
        var clicEvent = new MouseEvent('click', {
            'view': window,
            'bubbles': true,
            'cancelable': true
        });
        guardar.dispatchEvent(clicEvent);

        //Al final removemos el DataURL para liberar recursos
        URL.revokeObjectURL(guardar.href);

    }

    //Al hacer clic: generar el archivo
    document.querySelector('#boton-descargar').addEventListener('click', (e) =>{

        //Primero generamos el Blob
        const archivo = generarBlobXml();

        //Y luego lo descargamos
        descargarArchivo(archivo, 'datosPersonales.txt');

    });

});

La mayor parte del código funciona igual que en mi artículo anterior (Descargar Archivos con Javascript), así que pasemos a lo interesante: la función generarBlobXml.

Esta función comienza creando un xmlDocument, que básicamente tiene la misma funcionalidad y comportamiento que el objeto document que encontramos en el navegador. La función constructora requiere tres parámetros: Un espacio de Nombres, un Nombre Calificado, y el Tipo de Documento. Como este es un ejemplo sencillo no ahondaremos en los parámetros; por ahora solo debes saber que con los parámetros del ejemplo el objeto generado es de tipo xmlDocument.

Lo siguiente será llenar el documento con los datos. De forma referencial, este será el XML que finalmente vamos a generar:

 <datos>
	<nombre>Juan Carlos Gonzales</nombre>
	<fecha-nacimiento formato="yyyy-MM-dd">1982-01-01</fecha-nacimiento>
	<telefono>0800-999.0123</telefono>
	<direccion>Av. Principal con calle 18, Urb. La Esmeralda. Edificio Roma & paris, Apto. #12-B</direccion>
	<comentario>El documento debe contener caracteres <especiales> para mostrar como los convierte en el XML generado.</comentario>
</datos>

El primer paso será crear un elemento raíz, usando la función documento.createElement con un solo parámetro, el nombre de la etiqueta principal datos, la cual nos devolverá un objeto Element. Pero siempre recuerda que createElement sólo crea el nuevo elemento, y después tienes que usar appendChild para agregarlo a su elemento padre, que en este caso será el mismo documento.

Ahora que tenemos el elemento raíz, agregamos el nombre del usuario: usamos de nuevo la función documento.createElement para crear el elemento nombre. La función textContent nos permite asignar un texto al elemento, sin tener que preocuparnos por «escapar» manualmente los caracteres especiales. Ahora, para generar la estructura de árbol que queremos, debemos usar la función appendChild pero no en el documento, sino en la raíz que creamos en el paso anterior.

El elemento fecha-nacimiento lo crearemos del mismo modo, pero como demostración también le agregaremos un atributo con la función setAttribute, a la cual le pasamos dos parámetros: el nombre del atributo, y el valor que queremos almacenar en él.

Al final agregamos las últimas tres etiquetas telefono, direccion, y comentario, y así el documento está completo. Para obtener el documento como texto usamos la función outerHTML de elemento raíz, y con este generamos el Blob que necesitamos.

Fíjate también que para generar el Blob le pasamos el Tipo MIME text/xml. Este MIME es apropiado si estamos produciendo un XML con estructura genérica «legible para humanos»; si por el contrario, la estructura no es legible, sino pensada para ser leída por un programa, es recomendable usar un tipo específico según la aplicación asociada (como image/svg+xml para imágenes SVG) o el tipo genérico application/xml.

Para probar, guarda este código con el nombre script.js, en la misma carpeta que el archivo html, y ejecútalo de nuevo en el navegador: al presionar «Descargar Archivo» se muestra el cuadro de diálogo de descarga:

Ejemplo de descarga de archivo XML
Cuadro de Diálogo de descarga de archivo

Bueno, ya probamos la descarga de archivos de texto, hagamos ahora una prueba rápida con un archivo binario

Crear y Descargar un archivo de imagen

El proceso para descargar un archivo de imagen es el mismo que para los archivos de texto, la única diferencia está en el modo en que obtengamos la imagen: mediante un input[type=file], mediante AJAX desde el servidor, dibujado por nosotros en un canvas, etc.

Vamos a hacer una prueba rápida descargando una imagen simple que dibujaremos en un canvas. Veamos el HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VT - Crear archivos XML y de imagen con Javascript - Parte 2</title>
    <style>
        #elementoCanvas{
            width: 200px;
            height: 150px;
            margin: 1rem;
            border: 1px solid #404040;
            background-color: white;
        }
    </style>
</head>
<body>
    <form class="datos-personales">
        <canvas id="elementoCanvas" width="600" height="400"></canvas>
        <br/>
        <button type="button" id="boton-descargar">Descargar Archivo</button>
    </form>
    <script src="script-imagen.js"></script>
</body>
</html>

En el ejemplo, vemos que hay un elemento canvas, con un tamaño y color de borde y fondo aplicado por CSS. Estos estilos son simplemente para mejorar para apariencia del canvas en pantalla, pero no afectan el contenido de la imagen a generar. Por otro lado, los atributos width y height del elemento canvas si son importantes, porque indican la el tamaño en píxeles de la imagen que se generará. Estos se pueden indicar directamente en el HTML, o mediante javascript antes de comenzar a dibujar. Veamos ahora el código.

document.addEventListener('DOMContentLoaded', (e)=>{

    /**
     * Genera un objeto Blob, que contiene un archivo XML.
     * @param {function(Blob)} callback Función que se debe ejecutar con el blob generado.
     * @param {string} tipo Tipo de imagen a generar: 'image/png' o 'image/jpeg' 
     * @param {number} calidad Porcentaje de calidad de la imagen, entre 0 y 1.
     * @returns Blob Objeto de tipo Blob con el archivo xml.
     */
    const generarBlobImagen = function(callback, tipo, calidad){

        //primero obtenemos una referencia al elemento canvas
        const canvas = document.getElementById("elementoCanvas");

        //Pintamos algo sobre el canvas, para tener algo que mostrar:
        const ancho = canvas.width;
        const alto = canvas.height;

        //Fondo azul claro
        const contexto = canvas.getContext('2d');
        contexto.fillStyle = "#99D9FF"; //Azul claro
        contexto.fillRect(0, 0, ancho, alto);
        //Un par de rectángulos naranja
        contexto.fillStyle = "#E8821C"; //Naranja
        contexto.fillRect(50, 100, ancho - 100, 100); 
        contexto.strokeStyle = "#902090"; //Morado
        contexto.lineWidth= 12;
        contexto.strokeRect(100, 180, 300, 160); 
        //Y algo de texto
        contexto.fillStyle = "#008000"; //Verde
        contexto.font = '100px sans-serif'
        contexto.fillText('Canvas!', 100, 100);

        //Nos aseguramos de indicar un tipo MIME, por defecto image/png
        tipo = tipo || 'image/png';
        //la calidad solo es necesaria si es una imagen jpeg, 
        //pero asignamos un valor por defecto de todas formas
        calidad = calidad || 0.95; 

        //La función "toBlob" requiere una función callback, 
        //por lo que generarBlobImagen no puede retornar el 
        //blob directamente.
        canvas.toBlob(callback, tipo, calidad)

    }

    /**
     * Inicia la descarga del archivo en el navegador. 
     * Dependiendo de la configuración del navegador, 
     * este descargará el archivo automáticamente, o mostrará
     * el cuadro de dialogo "Guardar Cómo"
     * @param {Blob} archivo Objeto tipo Blob para descargar. 
     * @param {string} nombre Nombre por defecto para el archivo
     */
    const descargarArchivo = function(archivo, nombre){

        //Creamos un link
        const guardar = document.createElement('a');
        //Le asigna el ObjectURL que "apunta" al Blob
        guardar.href = URL.createObjectURL(archivo);
        //Preferiblemente descargar (con el nombre indicado), 
        //sino, entonces que por lo menos abra en otra ventana
        guardar.download = nombre || 'archivo.dat'; 
        guardar.target = '_blank'; 
        
        //Simulamos un clic del usuario
        //no es necesario agregar el link al DOM
        var clicEvent = new MouseEvent('click', {
            'view': window,
            'bubbles': true,
            'cancelable': true
        });
        guardar.dispatchEvent(clicEvent);

        //Al final removemos el DataURL para liberar recursos
        URL.revokeObjectURL(guardar.href);

    }

    //Al hacer clic: generar el archivo
    document.querySelector('#boton-descargar').addEventListener('click', (e) =>{

        //Primero definamos los parámetros como variales, para mayor claridad
        const tipo = 'image/png'; //Prueba también con 'image/jpeg'
        const calidad = 0.95; // 95%, aunque si es PNG no se requiere

        //Ahora generamos el blob, pero la descarga se iniciará 
        //mediante una función callback
        generarBlobImagen((archivo) => {

            //Lo descargamos dentro del callback
            descargarArchivo(archivo, 'nuevaImagen.png');

        }, tipo, calidad);

    });

});

De la misma forma que en los ejemplos de archivos TXT y XML, la función generarBlobImagen se encargará de crear un objeto Blob a partir de una imagen que dibujaremos y tomaremos de un canvas.

Primero necesitamos una referencia al canvas. En este punto aun tienes chance de modificar el tamaño de la imagen, cambiando los valores de las propiedades width y height del canvas.

Para dibujar la imagen en el canvas usaremos un contexto de dibujo, que obtendremos mediante la función getContext. Esta nos pide que le indiquemos que tipo de contexto usaremos; en nuestro ejemplo indicaremos 2d para obtener un CanvasRenderingContext2D, pero también podríamos indicar webgl o webgl2 para obtener un contexto en 3D.

Desde aquí, las operaciones de dibujado del ejemplo son auto explicativas: El canvas por defecto tiene una imagen color negro 100% transparente de 600x400px, por lo que comenzamos por pintar todo el fondo con un rectángulo azul claro. Luego, dibujamos un rectángulo naranja relleno y otro sin relleno con borde morado, y terminamos con un texto color verde. Las coordenadas y tamaños de los elementos dibujados se calculan desde la esquina superior izquierda del canvas, y usando las medidas internas del mismo (independientemente de las medidas indicadas por CSS).

Una vez que la imagen está dibujada, vamos a lo interesante: obtener el Blob de la imagen, para lo cual utilizaremos la función del toBlob canvas. Esta requiere los mismos tres parámetros que le indicamos a la función generarBlobImagen: una función callback, el tipo de imagen a generar, y la calidad de la imagen. El caso es que como el procesamiento de la imagen para generar el Blob puede tomar mucho tiempo, la implementación interna de la función toBlob se ejecuta de forma asíncrona, y una vez que la operación termina esta se encarga de ejecutar el callback, el cual recibe como parámetro el Blob generado. Veamos de nuevo el manejador de eventos del botón de descarga, donde pedimos que se genere el Blob:

//Ahora generamos el blob, pero la descarga se iniciará 
//mediante una función callback
generarBlobImagen((archivo) => {

    //Lo descargamos dentro del callback
    descargarArchivo(archivo, 'nuevaImagen.png');

}, tipo, calidad);

En lugar de ejecutar las funciones generarBlobImagen y descargarArchivo de forma sucesiva, creamos una función callback en línea para ejecutar dentro a la segunda, usando el parámetro archivo que ahora contiene el Blob generado.

Canvas con la imagen dibujada y lista para descargar

Como hemos visto, una ves que hemos generado un Blob (o File), la descarga de un archivo por Javascript es bastante simple, e independiente del tipo de archivo a descargar.

Derecho de uso

Los contenidos generados por el autor de este artículo (explicaciones, código fuente, y archivos adjuntos creados por el autor) están disponibles bajo licencia CC BY-SA 3.0, y pueden ser usados, derivados y compartidos bajo los términos indicados en la misma. Los contenidos no generados por el autor de este artículo son propiedad de sus respectivos dueños y están regidos por las licencias que estos hayan dispuesto.


Una respuesta a “Crear Archivos con Javascript – XML e Imagen”

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Soluciones claras y simples



Ing. Industrial, dedicado a la programación en ASP.NET+VB, SQL y Javascript+AJAX; y un poco de Android, Kotling, y Unity 🙂
Valencia, Venezuela



¿QUIERES APOYARME?

¿Te ha sido de ayuda alguno de mis artículos? Generar contenido técnico requiere de tiempo y esfuerzo. Con tu colaboración me puedes ayudar a mantener mi blog activo y actualizado. Si quieres y puedes apoyarme has clic aquí:

https://paypal.me/roimergarcia


ENTRADAS RECIENTES