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.
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('http') const fs = require('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('http') const fs = require('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.