Vimos en el concepto anterior como crear un servidor web solo de páginas HTML. Ahora extenderemos nuestro servidor web de páginas estáticas a otros formatos de archivos.
Un sitio web como sabemos está constituido entre otro por archivos html, js, css, jpg, gif, mp3, mp4, ico etc.
Siempre que devolvemos un recurso a un cliente (normalmente el navegador) en la cabecera de respuesta indicamos el tipo de recurso devuelto:
respuesta.writeHead(200, {'Content-Type': 'text/html'}) respuesta.write(contenido) respuesta.end()
Debemos inicializar el valor 'Content-Type' con el tipo de recurso a devolver, como en el concepto anterior solo devolvíamos archivos html nos bastaba con indicar siempre el valor: 'text/html'
Los MIME Types son la manera estándar de mandar contenido a través de internet. Especifican a cada archivo con su tipo de contenido.
Según el tipo de archivo que retornamos indicamos un valor distinto a Content-Type. Ejemplos de valores:
Content-type: text/html Content-type: text/css Content-type: image/jpg Content-type: image/x-icon Content-type: audio/mpeg3 Content-type: video/mp4 etc.
MIME es un acrónimo que significa "Multipurpose Internet Mail Extensions" (Extensiones Multipropósito de Correo de Internet). MIME es un estándar de Internet que amplía el formato de correo electrónico para admitir contenido multimedia, como imágenes, audio, video y otros tipos de datos no textuales, además del texto simple.
El Protocolo MIME se utiliza para especificar el tipo de contenido de un mensaje de correo electrónico o de los datos transmitidos a través de protocolos como HTTP utilizado en navegadores web.
Un listado bastante completo de tipos MIME lo podemos ver en este sitio.
Confeccionaremos un sitio que contenga una serie de archivos html, css, jpg, mp3, mp4 e ico.
Crearemos un directorio llamado ejercicio10 y dentro de este otro directorio interno llamado static donde dispondremos todos los archivos html,css, jpg, mp3, mp4 e ico.
En el directorio ejercicio10 tipearemos nuestra aplicacion Node.js que tiene por objetivo servir las páginas HTML y otros recursos que pidan los navegadores web.
La aplicación Node.js la llamamos ejercicio10.js
ejercicio10.js
const http = require('node:http') const fs = require('node:fs') const mime = { 'html': 'text/html', 'css': 'text/css', 'jpg': 'image/jpg', 'ico': 'image/x-icon', 'mp3': 'audio/mpeg3', 'mp4': 'video/mp4' } const servidor = http.createServer((pedido, respuesta) => { const url = new URL('http://localhost:8888' + pedido.url) let camino = 'static' + url.pathname if (camino == 'static/') camino = 'static/index.html' fs.stat(camino, error => { if (!error) { fs.readFile(camino, (error, contenido) => { if (error) { respuesta.writeHead(500, { 'Content-Type': 'text/plain' }) respuesta.write('Error interno') respuesta.end() } else { const vec = camino.split('.') const extension = vec[vec.length - 1] const mimearchivo = mime[extension] respuesta.writeHead(200, { 'Content-Type': mimearchivo }) respuesta.write(contenido) respuesta.end() } }) } else { respuesta.writeHead(404, { 'Content-Type': 'text/html' }) respuesta.write('<!doctype html><html><head></head><body>Recurso inexistente</body></html>') respuesta.end() } }) }) servidor.listen(8888) console.log('Servidor web iniciado')
Este proyecto lo puede descargar en un zip con todos los archivos js, html, jpg etc. desde este enlace : ejercicio10
Veamos los cambios que debemos hacer con respecto al concepto anterior que servíamos solo archivos HTML.
Primero definimos un objeto literal asociando las distintas extensiones de archivos y su valor MIME:
const mime = { 'html': 'text/html', 'css': 'text/css', 'jpg': 'image/jpg', 'ico': 'image/x-icon', 'mp3': 'audio/mpeg3', 'mp4': 'video/mp4' }
Todo el resto del código es idéntico al concepto anterior salvo cuando tenemos que retornar el recurso que solicita el navegador:
const vec = camino.split('.') const extension = vec[vec.length - 1] const mimearchivo = mime[extension] respuesta.writeHead(200, { 'Content-Type': mimearchivo }) respuesta.write(contenido) respuesta.end()
Descomponemos en un vector separando por el punto la constante camino:
const vec = camino.split('.')
Si tenemos en la constante camino el valor:
/pagina1.htmlLuego de ejecutarse el método split pidiendo que separa por el caracter punto tenemos en la constante vec dos elementos:
vec[0] tiene almacenado /pagina1 vec[1] tiene almacenado html
Sacamos el último elemento del vector que en definitiva almacena la extensión del archivo:
const extension = vec[vec.length - 1]
Seguidamente rescatamos la propiedad del objeto literal mime:
const mimearchivo = mime[extension]
Por ejemplo si tenemos en la variable extension el valor 'html' luego en la variable mimearchivo se almacena: 'text/html' que es el valor para dicha propiedad definida en la variable mime.
Ahora llamamos al métdodo writeHead donde retornamos el tipo MIME para dicha extensión:
respuesta.writeHead(200, { 'Content-Type': mimearchivo })
El resto de este programa no sufre cambios con respecto a devolver siempre archivos HTML.
Luego de iniciar la aplicación desde la consola:
Ya podemos solicitar páginas al servidor que acabamos de inicializar y comprobar que nos retorna los distintos recursos enumerados dentro de cada página (imágenes, audios, videos, hojas de estilo etc.):
La organización de los archivos en el disco duro es:
Como vemos en la carpeta ejercicio10 se ubica la aplicación Node.js y la carpeta static con todos los recursos:
En nuestro ejemplo hemos limitado a servir 6 formatos de archivos distintos:
const mime = { 'html' : 'text/html', 'css' : 'text/css', 'jpg' : 'image/jpg', 'ico' : 'image/x-icon', 'mp3' : 'audio/mpeg3', 'mp4' : 'video/mp4' }
Para servir otros formatos de archivos deberíamos agregar al objeto literal mime los valores respectivos que define el protocolo MIME.
ejercicio10b.js
const http = require('node:http') const fs = require('node:fs/promises') const mime = { 'html': 'text/html', 'css': 'text/css', 'jpg': 'image/jpg', 'ico': 'image/x-icon', 'mp3': 'audio/mpeg3', 'mp4': 'video/mp4' } const servidor = http.createServer((pedido, respuesta) => { const url = new URL('http://localhost:8888' + pedido.url) let camino = 'static' + url.pathname if (camino == 'static/') camino = 'static/index.html' fs.stat(camino) .then(() => { fs.readFile(camino) .then(contenido => { const vec = camino.split('.') const extension = vec[vec.length - 1] const mimearchivo = mime[extension] respuesta.writeHead(200, { 'Content-Type': mimearchivo }) respuesta.write(contenido) respuesta.end() }) .catch(error => { respuesta.writeHead(500, { 'Content-Type': 'text/plain' }) respuesta.write('Error interno') respuesta.end() }) }) .catch(error => { respuesta.writeHead(404, { 'Content-Type': 'text/html' }) respuesta.end('<h1>Error 404: No existe el recurso solicitado</h1>') }) }) servidor.listen(8888) console.log('Servidor web iniciado')
Como podemos comprobar al utilizar las funciones que retornan promesas nos permiten generar un código mucho más legible.