Introducción a Node.js, parte 6: Envío de archivos

En las últimas dos publicaciones hemos visto cómo enviar datos al servidor a través de los formularios mediante los métodos GET y POST, pero aún no hemos visto otra de las formas más utilizadas en las aplicaciones web, el envío de archivos mediante un formulario.

Por empezar vamos a crear un nuevo proyecto al que yo en mi caso llamaré subida_archivos. Dentro del mismo crearemos un archivo llamado index.js y dos carpetas: archivos_subidosmodulos. Y dentro de la carpeta modulos crearemos otro archivo llamado subida_archivos.js.

Vamos a editar el archivo subida_archivos.js con el siguiente código:

function dibuarCabecera(){
   var html = '<!DOCTYPE html>';
   html += '<html>';
   html += '<head>';
   html += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">';
   html += '</head>';
   html += '<body>';
   return html;
}
function dibujarPie(){
   var html = '</body>';
   html += '</html>';
   return html;
}
function dibujarFormulario(){
   var html = dibuarCabecera();
   html += '<form method="post" enctype="multipart/form-data">';
   html += '<label> Archivo: </label>';
   html += '<input type="file" name="file" />';
   html += '<input type="submit" value="Subir" />';
   html += '</form>';
   html += dibujarPie();
   return html;
}
function responderSubida(archivo_subido){
   var html = dibuarCabecera();
   if(archivo_subido){
      html += '<p> <strong> El archivo ha sido subido correctamente. <strong> </p>';
   }else{
      html += '<p style="color: #f00;"> Error al intentar subir el archivo. </p>';
   }
   html += '<p> <a href="/"> Volver </a> </p>';
   html += dibujarPie();
   return html;
}
exports.dibujarFormulario = dibujarFormulario;
exports.responderSubida = responderSubida;

Nada del otro mundo, funciones que nos devolverán código html.

Las funciones dibuarCabecera()dibujarPie() nos devolverán la parte de arriba y abajo del código html de la página respectivamente.

También tendremos una función dibujarFormulario() que nos devolverá el código html de un formulario con un campo para subir un archivo y enviarlo al servidor:

function dibujarFormulario(){
   var html = dibuarCabecera();
   html += '<form method="post" enctype="multipart/form-data">';
   html += '<label> Archivo: </label>';
   html += '<input type="file" name="file" />';
   html += '<input type="submit" value="Subir" />';
   html += '</form>';
   html += dibujarPie();
   return html;
}

Algo que es no está de más decirlo es que el formulario para estar preparado de enviar archivos debemos agregarle el atributo enctype con el valor “multipart/form-data”:

enctype="multipart/form-data"

Y una función llamada responderSubida() que recibirá un parámetro boolean, será true si el archivo pudo subirse al servidor y false si hubo algún error:

function responderSubida(archivo_subido){
   var html = dibuarCabecera();
   if(archivo_subido){
      html += '<p> <strong> El archivo ha sido subido correctamente. <strong> </p>';
   }else{
      html += '<p style="color: #f00;"> Error al intentar subir el archivo. </p>';
   }
   html += '<p> <a href="/"> Volver </a> </p>';
   html += dibujarPie();
   return html;
}

Por último exportaremos las funciones dibujarFormulario()responderSubida() para que estén disponibles al instanciarse el módulo:

exports.dibujarFormulario = dibujarFormulario;
exports.responderSubida = responderSubida;

La primer función la llamaremos para mostrar el formulario y así el usuario pueda cargar el archivo y la segunda para devolver un mensaje que será true o false dependiendo de si se ha podido o no subir el archivo.

Bien, ahora para poder subir y guardar archivos al servidor necesitamos el módulo formidable, este módulo no viene con Node así que tendremos que instalarlo desde la consola. Entonces vamos a abrir la consola y a escribir lo siguiente:

npm install -g formidable

Bien, ahora que tenemos instalado el módulo formidable vamos a editar el archivo index.js con el siguiente código:

var http = require('http');
var formidable = require('formidable');
var subida_archivos = require('./modulos/subida_archivos');
http.createServer(function(peticion, respuesta){
   if(peticion.method == 'POST'){
      //Creamos una instancia de IncomingForm.
      var incoming = new formidable.IncomingForm();
      //Carpeta donde se guardarán los archivos.
      incoming.uploadDir = 'archivos_subidos';
      //Parseamos la petición.
      incoming.parse(peticion);
      //Se dispara en caso de que haya algún error.
      incoming.on('error', function(err) {
         respuesta.writeHead(200, {'Content-Type': 'text/html'});
         respuesta.end(subida_archivos.responderSubida(false));
      });
      //Se dispara cuando el archivo llegó al servidor.
      incoming.on('file', function(field, file){
         console.log('Archivo recibido');
      });
      //Se dispara antes de guardar el archivo.
      incoming.on('fileBegin', function(field, file){
         if(file.name){
            //Modificamos el nombre del archivo por código al azar más "_nombre original del archivo"
            file.path += '_' + file.name;
         }
      });
      //Se dispara una vez que los archivos fueron guardados.
      incoming.on('end', function(){
         respuesta.writeHead(200, {'Content-Type': 'text/html'});
         respuesta.end(subida_archivos.responderSubida(true));
      });
   }else{
      respuesta.writeHead(200, {'Content-Type': 'text/html'});
      respuesta.end(subida_archivos.dibujarFormulario());
   }
}).listen(3000, '127.0.0.1');
console.log('Servidor funcionando.');

Bueno, ahora vamos a explicar el código.

En primer lugar importamos el módulo formidable:

var formidable = require('formidable');

Además del módulo httpsubida_archivos, este último es el módulo que creamos anteriormente para mostrar las respuestas html.

No vamos a explicar todo el código ya que a estas alturas deberíamos entender todo, excepto por estas líneas:

if(peticion.method == 'POST'){
   //Creamos una instancia de IncomingForm.
   var incoming = new formidable.IncomingForm();
   //Carpeta donde se guardarán los archivos.
   incoming.uploadDir = 'archivos_subidos';
   //Parseamos la petición.
   incoming.parse(peticion);
   //Se dispara en caso de que haya algún error.
   incoming.on('error', function(err) {
      respuesta.writeHead(200, {'Content-Type': 'text/html'});
      respuesta.end(subida_archivos.responderSubida(false));
   });
   //Se dispara cuando el archivo llegó al servidor.
   incoming.on('file', function(field, file){
      console.log('Archivo recibido');
   });
   //Se dispara antes de guardar el archivo.
   incoming.on('fileBegin', function(field, file){
      if(file.name){
         //Modificamos el nombre del archivo por código al azar más "_nombre original del archivo"
         file.path += '_' + file.name;
      }
   });
   //Se dispara una vez que los archivos fueron guardados.
   incoming.on('end', function(){
      respuesta.writeHead(200, {'Content-Type': 'text/html'});
      respuesta.end(subida_archivos.responderSubida(true));
   });
}

Aquí verificamos si está llegando una petición por POST, osea si se ha enviado el formulario.

Primero creamos una instancia del objeto IncomingForm():

var incoming = new formidable.IncomingForm();

Definimos la carpeta donde serán guardados los archivos:

incoming.uploadDir = 'archivos_subidos';

Y parseamos la petición:

incoming.parse(peticion);

Y ahora iremos a la acción, mediante .on() llamaremos a los eventos que se dispararán en los distintos estados de la subida del archivo:

incoming.on('error', function(err) {
   respuesta.writeHead(200, {'Content-Type': 'text/html'});
   respuesta.end(subida_archivos.responderSubida(false));
});

Este evento se disparará si hay algún error al subir el archivo en el servidor.

incoming.on('file', function(field, file){
   console.log('Archivo recibido');
});

Una vez que el archivo llegue al servidor, se disparará este evento.

incoming.on('fileBegin', function(field, file){
   if(file.name){
      //Modificamos el nombre del archivo por código al azar más "_nombre original del archivo"
      file.path += '_' + file.name;
   }
});

Aquí se ejecutará un evento antes de que el archivo sea guardado dentro de la carpeta correspondiente. En nuestro caso modificamos el nombre del archivo. Esto se debe a que los archivos por defecto se guardan con una cadena alfanumérica al azar, pero sin el nombre ni su extensión, aquí simplemente concatenamos esa cadena alfanumérica con el nombre del archivo junto con la extensión del mismo.

Y finalmente creamos el evento ‘end’ que se disparará una vez que el archivo haya sido guardado correctamente:

incoming.on('end', function(){
   respuesta.writeHead(200, {'Content-Type': 'text/html'});
   respuesta.end(subida_archivos.responderSubida(true));
});

Bueno, ahora ya podemos iniciar nuestro servidor y probar que todo funcione.

Saludos!

Anterior: Introducción a Node.js, parte 5: Enviar formularios por POST

Siguiente: Introducción a Node.js, parte 7: Instalar Express


6 Respuestas a “Introducción a Node.js, parte 6: Envío de archivos”

  1. Muy buenas, Enhorabuena por el excelente tutorial.

    Me surge una duda, después de seguir tus pasos, al compilar el código y crear el servidor me lanza un error porque no encuentra el modulo de formidable.. sabes a que se puede deber o como solucionarlo?¿

    Gracias.

    1. Claro, porque para eso necesitás ese módulo.

      Una pregunta vos lo instalaste en la consola? Tenés que escribir:

      npm install -g formidable

      Fijate con eso.

      Saludos!

      1. Ya está!! el problema estaba que me lo habia instalado en una ubicación diferente, con lo que al usar el require(formidable) tuve que poner la ruta entera en la que me lo habia instalado. Ahora ya sube archivos perfectamente. Muchas gracias! Voy a por el siguiente!

  2. Muy buenos los tutoriales, funcionan perfectamente y se encuentran explicados de una manera clara y práctica.

    Estarían bárbaro unos tutoriales sobre programación para Android.

    Gracias.