Cómo cargar un archivo usando XMLHttpRequest (XHR) y Node.js

La carga de archivos es la funcionalidad más común en las aplicaciones web modernas. Muchas aplicaciones permiten a los usuarios cargar un avatar o un archivo adjunto para realizar algunas tareas de back-end. Escribir el código para cargar un archivo de forma asíncrona en un servidor parece una tarea desafiante.

En este artículo, explicaré cómo crear un HTML formulario, envíe los archivos seleccionados al servidor con JavaScript y procesa el archivo de carga en Node.js .

Creación de un formulario HTML

Comencemos a construir un formulario HTML simple que tiene dos elementos:un <input> etiqueta para permitir al usuario seleccionar un archivo de la computadora local y un <button> etiqueta para enviar el formulario. Así es como se ve:

<form method="POST" enctype="multipart/form-data">
    <input type="file" name="file">
    <button type="submit" role="button">Upload File</button>
</form>

Enviar datos de formulario con JavaScript

En este momento, si hace clic en el botón Enviar, el formulario se publica en sí mismo como action el atributo no está definido. Queremos asegurarnos de que cuando se envía el formulario, el archivo seleccionado se carga en el servidor de forma asíncrona (sin actualizar la página) utilizando el objeto JavaScript XHR.

Vamos a crear un nuevo archivo llamado upload.js y agréguele una referencia en su archivo HTML:

<script src="upload.js"></script>

A continuación, defina dos variables. La URL donde desea publicar los datos del formulario y el elemento DOM para el formulario:

// define URL and for element
const url = "http://localhost:3000/upload-avatar";
const form = document.querySelector('form');

Ahora agregue un detector de eventos al formulario para capturar el evento de envío del formulario. Además, asegúrese de que la acción predeterminada no se active:

// add event listener
form.addEventListener('submit', e => {

    // disable default action
    e.preventDefault();

    // ....
});

A continuación, cree una nueva instancia de FormData y agregue el archivo seleccionado:

// collect files
const files = document.querySelector('[name=file]').files;
const formData = new FormData();
formData.append('avatar', files[0]);

// ....

Finalmente, use el objeto XHR integrado para enviar los datos a la URL que definimos anteriormente e imprima la respuesta en la consola:

// post form data
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';

// log response
xhr.onload = () => {
    console.log(xhr.response);
};

// create and send the reqeust
xhr.open('POST', url);
xhr.send(formData);

Aquí está el upload.js completo archivo:

// define URL and for element
const url = "/upload-avatar";
const form = document.querySelector('form');

// add event listener
form.addEventListener('submit', e => {

    // disable default action
    e.preventDefault();

    // collect files
    const files = document.querySelector('[name=file]').files;
    const formData = new FormData();
    formData.append('avatar', files[0]);

    // post form data
    const xhr = new XMLHttpRequest();

    // log response
    xhr.onload = () => {
        console.log(xhr.responseText);
    };

    // create and send the reqeust
    xhr.open('POST', url);
    xhr.send(formData);
});

La respuesta devuelta por la API de Node.js es un objeto JSON. En este momento, solo estamos imprimiendo la respuesta a la consola. Eche un vistazo a esta guía para conocer varias formas de manejar la respuesta JSON en XHR.

Procesamiento de datos de formulario con Node.js

Para manejar la carga de archivos en el lado del servidor a través de Node.js y Express, ya he escrito un artículo detallado. No entraré en detalles sobre cómo configurar la aplicación Node.js e instalar todos los paquetes necesarios. Consulte el tutorial para aprender todas estas cosas.

El tutorial utiliza el middleware express-fileupload para manejar multipart/form-data solicitudes, extrae los archivos si están disponibles y los pone a disposición en req.files propiedad.

Puedes instalar express-fileupload en su proyecto escribiendo el siguiente comando:

$ npm install express-fileupload --save

A continuación, agregue la siguiente ruta Express para procesar y guardar el archivo enviado por el código JavaScript anterior:

app.post('/upload-avatar', async (req, res) => {
    try {
        if(!req.files) {
            res.send({
                status: false,
                message: 'No file uploaded'
            });
        } else {
            // use the name of the input field (i.e. "avatar") 
            // to retrieve the uploaded file
            let avatar = req.files.avatar;
            
            // use the mv() method to place the file in 
            // upload directory (i.e. "uploads")
            avatar.mv('./uploads/' + avatar.name);

            //send response
            res.send({
                status: true,
                message: 'File is uploaded'
            });
        }
    } catch (err) {
        res.status(500).send(err);
    }
});

El código anterior es el código mínimo requerido para manejar archivos en una aplicación Node.js.

Subir varios archivos

El ejemplo anterior explica cómo cargar un solo archivo en JavaScript. ¿Qué pasa si quieres subir varios archivos? ¿En seguida? Sin preocupaciones. Con algunos cambios, podemos ajustar el código anterior para admitir la carga de varios archivos.

En primer lugar, actualice el <input> etiqueta para permitir al usuario seleccionar varios archivos:

<input type="file" name="file" multiple>

Cambie la URL de envío del formulario a la que maneja la carga de múltiples archivos:

const url = "http://localhost:3000/upload-photos";

A continuación, actualice el FormData part para enviar todos los archivos seleccionados en lugar de solo uno:

Array.from(files).forEach(file => {
    formData.append("photos", file);
});

Finalmente, cree una nueva ruta Express que acepte múltiples archivos y súbalos al servidor:

app.post('/upload-photos', async (req, res) => {
    try {
        if (!req.files) {
            res.send({
                status: false,
                message: 'No file uploaded'
            });
        } else {
            let data = [];

            //loop all files
            _.forEach(_.keysIn(req.files.photos), (key) => {
                let photo = req.files.photos[key];

                //move photo to uploads directory
                photo.mv('./uploads/' + photo.name);
            });

            //return response
            res.send({
                status: true,
                message: 'Files are uploaded'
            });
        }
    } catch (err) {
        res.status(500).send(err);
    }
});

¡Felicidades! Ahora puede cargar cualquier número de archivos a la vez.

Conclusión

Eso es todo amigos. En este tutorial, aprendió a cargar un archivo usando JavaScript incorporado XMLHttpRequest object y Node.js en el lado del servidor. Analizamos tanto la carga de un solo archivo como la carga de varios archivos a la vez.

El objetivo de este artículo fue explicar los pasos básicos necesarios para cargar correctamente un archivo en JavaScript y Node.js. Para una aplicación del mundo real que se ejecuta en el servidor de producción, debe haber algunos pasos de validación. El usuario debería mostrar un error si selecciona un tipo de archivo incorrecto o si el tamaño del archivo excede el límite permitido.

XMLHttpRequest también proporciona eventos para rastrear el progreso de carga y descarga de archivos. Consulte esta guía para obtener más información al respecto.

Eche un vistazo a esta guía para aprender a manejar la carga de archivos a través de Fetch API, una alternativa moderna a XHR.