Sirve imágenes de proyectos con jsDelivr y GitHub Pages
Para un pequeño proyecto público, puedes servir una imagen de GitHub mediante una URL de jsDelivr fijada a un commit. Este tutorial prepara un PNG, publica el archivo generado y añade una copia opcional en GitHub Pages con un mecanismo de respaldo en JavaScript. Ambas URL apuntarán a la misma imagen.
Elige recursos adecuados para los servicios
jsDelivr es un servicio de CDN, no una biblioteca de JavaScript que debas instalar. Su endpoint de GitHub recupera los archivos del repositorio directamente; GitHub Pages no es un requisito previo. Pages y jsDelivr son alternativas para entregar los archivos. Ninguno cambia el tamaño ni vuelve a comprimir el PNG en este ejemplo.
Usa esta configuración de CDN gratuita de imágenes para recursos de un proyecto público, como una captura de pantalla de una demo de mapas de código abierto. La política de uso de jsDelivr prohíbe el alojamiento de archivos o contenido multimedia de uso general, incluido el almacenamiento de las subidas de un sitio de alojamiento de imágenes. Reconoce explícitamente proyectos legítimos, como aplicaciones y juegos con recursos de imagen. Proporciona documentación pública y una licencia adecuada para tu proyecto, y publica solo imágenes que puedas distribuir.
GitHub Pages está disponible para repositorios públicos en GitHub Free. Sus límites incluyen un máximo de 1 GB para el sitio publicado y un límite flexible de ancho de banda de 100 GB al mes. Pages también restringe el uso del servicio para operar un negocio en línea, un sitio de comercio electrónico o un SaaS comercial. Estos servicios no son adecuados para subidas privadas ni para un negocio de alojamiento de imágenes de uso general.
Prepara un PNG para publicarlo
Empieza en una copia local de tu proyecto público, en su rama main.
El repositorio del ejemplo se llama map-demo; reemplaza
YOUR-USERNAME y map-demo en las URL por tu cuenta y repositorio.
Este tutorial presupone un proyecto con Yarn 4, Node.js 24 o posterior, un shell POSIX y ningún sitio
existente en docs/. El flujo de trabajo local de imágenes se probó con
Node.js 26.8.1 y sharp 0.35.4 en Linux.
Instala el procesador de imágenes sharp y crea los directorios de entrada y publicación:
corepack yarn add --dev --exact sharp@0.35.4 &&
mkdir -p original-images docs/images
Coloca una captura de pantalla PNG estática en sRGB de 8 bits en original-images/map.png.
El HTML que aparece a continuación presupone que mide 640 × 360 píxeles; cambia las dimensiones y
el texto alternativo del HTML para que coincidan con tu imagen.
Guarda lo siguiente como optimize.cjs en la raíz del repositorio. Procesa los
archivos .png ubicados directamente en original-images/
y escribe archivos con los mismos nombres en un nuevo directorio de versión:
const fs = require('node:fs/promises')
const path = require('node:path')
const sharp = require('sharp')
async function optimizeImage(inputPath, outputPath) {
const image = sharp(inputPath)
const metadata = await image.metadata()
if (metadata.format !== 'png') throw new Error(`Expected a PNG: ${inputPath}`)
await image.png({ compressionLevel: 9, palette: false }).toFile(outputPath)
}
async function processDirectory(inputDir, outputDir) {
const files = await fs.readdir(inputDir)
// A published version must not be overwritten by a later run.
await fs.mkdir(outputDir)
for (const file of files) {
const inputPath = path.join(inputDir, file)
const stat = await fs.stat(inputPath)
if (!stat.isFile() || !file.endsWith('.png')) continue
await optimizeImage(inputPath, path.join(outputDir, file))
}
}
processDirectory('original-images', 'docs/images/v1').catch((error) => {
console.error(error)
process.exitCode = 1
})
Ejecútalo una vez:
corepack yarn node optimize.cjs
Abre docs/images/v1/map.png y compáralo con el original. El script conserva sus dimensiones
y usa compresión PNG sin cuantización de paleta. Normalmente, sharp convierte a sRGB y elimina los
metadatos. No se garantiza un archivo más pequeño: compara los tamaños antes de adoptar el resultado.
Configurar quality para PNG habilitaría la cuantización de paleta y podría
provocar una pérdida de colores; consulta las
opciones de salida de sharp.
Una nueva ejecución falla si docs/images/v1 ya existe, y deja esa versión intacta.
Una entrada dañada también provoca que la ejecución termine con un error; puede dejar un nuevo
directorio escrito parcialmente. No publiques ese directorio. Después de corregir la entrada,
elimina solo el directorio de salida fallido y no publicado antes de volver a intentarlo.
Incluye la imagen generada en un commit
Comprueba que docs/images/v1/map.png sea el PNG real, no un puntero de Git LFS. Incluye el
propio recurso generado en un commit para que jsDelivr pueda recuperarlo. Desde la raíz del
repositorio, sin cambios ajenos a esta tarea en el área de preparación:
git add docs/images/v1/map.png &&
git commit -m "Add versioned map screenshot" &&
git push origin main &&
git rev-parse HEAD
Conserva el hash completo del commit que imprime el último comando. A continuación,
COMMIT-SHA representa ese hash, del commit que contiene el PNG. Conserva también
optimize.cjs, package.json y yarn.lock junto
con el código fuente de tu proyecto; node_modules/ no debe formar parte del commit.
Usa la URL de jsDelivr fijada al commit
La URL de tu imagen es:
https://cdn.jsdelivr.net/gh/YOUR-USERNAME/map-demo@COMMIT-SHA/docs/images/v1/map.png
Ábrela en un navegador después de reemplazar los marcadores de posición. Debería mostrar el PNG generado. No se requiere una cuenta de jsDelivr ni un despliegue de Pages. Este es el formato de URL de GitHub documentado.
Usa un hash de commit completo en lugar de main,
latest o una versión omitida. jsDelivr
almacena permanentemente en caché las versiones estáticas y las URL de commits
y les asigna encabezados de caché de larga duración. Publica las imágenes modificadas en un nuevo
commit y actualiza la URL; eliminar el original de GitHub no garantiza que se retire una copia
almacenada en caché.
Añade GitHub Pages como respaldo opcional
Puedes usar la URL de jsDelivr en tu propio sitio de inmediato. Para darle a este ejemplo una
segunda vía de entrega, publica docs/ a través de Pages. Guarda esta página
como docs/index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Map demo</title>
<script src="image.js" defer></script>
</head>
<body>
<h1>Map demo</h1>
<img id="my-image" alt="Screenshot of the map demo" width="640" height="360" />
<noscript>Enable JavaScript to load this image demo.</noscript>
</body>
</html>
Fiabilidad y mecanismo de respaldo
Guarda lo siguiente como docs/image.js. Reemplaza YOUR-USERNAME
y COMMIT-SHA, y reemplaza map-demo si tu repositorio tiene
otro nombre. Usa el hash del commit de la imagen del paso anterior; puedes incluir la página y el
script en un commit más adelante.
function loadImage(imageElement, primarySrc, fallbackSrc) {
imageElement.onerror = function () {
imageElement.onerror = null
console.warn('Primary CDN failed, using fallback')
imageElement.src = fallbackSrc
}
imageElement.src = primarySrc
}
const img = document.getElementById('my-image')
if (!(img instanceof HTMLImageElement)) throw new Error('Missing image element: my-image')
loadImage(
img,
'https://cdn.jsdelivr.net/gh/YOUR-USERNAME/map-demo@COMMIT-SHA/docs/images/v1/map.png',
'https://YOUR-USERNAME.github.io/map-demo/images/v1/map.png',
)
El manejador cambia a Pages una sola vez si se produce un error con la imagen. Si ambos orígenes fallan, deja disponible el texto alternativo de la imagen y deja de reintentar. No tiene un tiempo límite para una solicitud que sigue esperando, y las dos vías siguen compartiendo GitHub como fuente. Es un mecanismo de respaldo limitado, no una garantía de disponibilidad.
Crea un archivo docs/.nojekyll vacío y, con él,
desactiva el procesamiento de Jekyll;
luego publica la página y el script:
touch docs/.nojekyll &&
git add docs/.nojekyll docs/index.html docs/image.js &&
git commit -m "Add image demo page" &&
git push origin main
En Settings del repositorio, abre
Pages. En Build and deployment,
establece Source en Deploy from a branch,
elige main y /docs, y haz clic en
Save. La
guía de publicación de GitHub
describe estos controles y la ejecución de despliegue que debes revisar si falla la publicación.
La publicación desde una rama usa un flujo de trabajo de Actions gestionado por GitHub; no necesitas
un flujo de trabajo de optimización personalizado.
Espera a que se complete un despliegue correctamente antes de abrir https://YOUR-USERNAME.github.io/map-demo/.
Esto presupone el dominio predeterminado del sitio del proyecto, sin un dominio personalizado.
Las rutas difieren porque Pages publica el contenido de docs/, mientras
que jsDelivr lee desde la raíz del repositorio:
| Ubicación | Ruta de la imagen |
|---|---|
| Archivo incluido en el commit | docs/images/v1/map.png |
jsDelivr, después de @COMMIT-SHA/ | docs/images/v1/map.png |
Pages, después de /map-demo/ | images/v1/map.png |
Para una actualización, cambia el directorio de salida del optimizador a
docs/images/v2, genera e inspecciona la nueva imagen e inclúyela en un commit.
Usa ese nuevo hash de commit y la ruta v2 en la URL de CDN del script,
y v2 en su URL de Pages. Despliega el nuevo recurso antes de cambiar
los clientes que lo utilizan. Conserva v1 para los enlaces antiguos.
Pages no fija una URL a un commit de Git: el directorio de versión solo permanece estable si
mantienes su contenido sin cambios.
Prueba tu configuración
Primero, abre ambas URL de imagen directamente. Un error 404 suele indicar que el archivo no se incluyó en la revisión fijada, que la ruta o el uso de mayúsculas y minúsculas difiere, o que Pages aún no ha desplegado la carpeta seleccionada. Comprueba que cada respuesta sea un PNG, no una página HTML de error.
En la página de demo, usa el inspector de red de tu navegador para confirmar la solicitud de la imagen y sus dimensiones. Bloquea la URL exacta de la imagen en jsDelivr y vuelve a cargar la página: la solicitud a Pages debería completarse correctamente. Luego bloquea ambas URL y vuelve a cargar: debería haber un intento para cada una, con el texto alternativo de la imagen aún presente. Desbloquéalas después. Prueba el comportamiento de carga de la imagen, no solo que la respuesta de la página sea correcta.
Seguridad
Encabezados CORS
Un <img> normal de otro origen puede mostrarse sin habilitar CORS.
Leer sus píxeles mediante canvas tiene
requisitos CORS adicionales. Esta demo solo muestra la imagen.
CORS no es un mecanismo de autenticación ni restringe quién puede descargar un recurso público.
Evita la inserción de imágenes mediante enlaces externos
El JavaScript de tu página no puede impedir que otra persona inserte la URL pública de la imagen. Mantén las imágenes privadas fuera de este flujo de trabajo. Para controlar el acceso, usa almacenamiento y un servicio de entrega que puedan exigir autorización o URL firmadas.
Cuando necesitas más de un tamaño de imagen
Este ejemplo publica un PNG con sus dimensiones originales. Añadir srcset
o un elemento <picture> no genera imágenes más pequeñas ni archivos AVIF/WebP:
esos archivos deben producirse e incluirse en un commit primero. Un mecanismo de respaldo adaptable
también debe eliminar los candidatos fallidos de srcset y
<source> antes de cambiar src, por lo que el
manejador sencillo anterior está diseñado intencionalmente para un solo
<img> sin esos candidatos. Para tamaños o formatos dinámicos, considera
un servicio de transformación de imágenes como la
API de procesamiento de imágenes de Transloadit.
