Sirva imagens de projeto com o jsDelivr e o GitHub Pages
Para um pequeno projeto público, você pode servir uma imagem do GitHub por meio de uma URL do jsDelivr fixada em um commit. Este passo a passo prepara um PNG, publica o arquivo gerado e adiciona uma cópia opcional no GitHub Pages com um fallback em JavaScript. As duas URLs apontarão para a mesma imagem.
Escolha arquivos adequados aos serviços
O jsDelivr é um serviço de CDN, não uma biblioteca JavaScript para instalar. O endpoint dele para o GitHub obtém os arquivos do repositório diretamente; o GitHub Pages não é um pré-requisito. O Pages e o jsDelivr são formas alternativas de entregar os arquivos. Nenhum dos dois redimensiona ou recomprime o PNG deste exemplo.
Use esta configuração gratuita de CDN de imagens para recursos que pertencem a um projeto público, como uma captura de tela em uma demonstração de mapa de código aberto. A política de uso do jsDelivr proíbe a hospedagem de arquivos ou mídia de uso geral, incluindo o armazenamento dos uploads de um site de hospedagem de imagens. Ela reconhece explicitamente projetos legítimos, como apps e jogos com recursos de imagem. Dê ao seu projeto documentação pública e uma licença adequada, e publique apenas imagens que você pode distribuir.
O GitHub Pages está disponível para repositórios públicos no GitHub Free. Os limites dele incluem um limite de 1 GB para o site publicado e um limite flexível de largura de banda de 100 GB por mês. O Pages também restringe o uso do serviço para operar um negócio online, um site de e-commerce ou um SaaS comercial. Esses serviços não são adequados para uploads privados nem para um negócio de hospedagem de imagens de uso geral.
Prepare um PNG para publicação
Comece em um checkout local do seu projeto público, na branch main. O repositório de exemplo
se chama map-demo; substitua YOUR-USERNAME e map-demo nas URLs pela sua conta e pelo seu
repositório. Este passo a passo pressupõe um projeto Yarn 4, Node.js 24 ou posterior, um shell POSIX
e nenhum site existente em docs/. O fluxo de trabalho local de imagens foi testado com Node.js
26.8.1 e sharp 0.35.4 no Linux.
Instale o processador de imagens sharp e crie os diretórios de entrada e de publicação:
corepack yarn add --dev --exact sharp@0.35.4 &&
mkdir -p original-images docs/images
Coloque uma captura de tela PNG estática, sRGB de 8 bits, em original-images/map.png. O HTML abaixo pressupõe
que ela tenha 640 × 360 pixels; altere as dimensões e o texto alternativo no HTML para corresponder
à sua imagem.
Salve isto como optimize.cjs na raiz do repositório. O script processa os arquivos .png
localizados diretamente em original-images/ e grava arquivos com os mesmos nomes em um novo diretório de
versão:
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
})
Execute-o uma vez:
corepack yarn node optimize.cjs
Abra docs/images/v1/map.png e compare com o original. O script preserva as dimensões da imagem e usa
compressão PNG sem quantização de paleta. Normalmente, o sharp converte para sRGB e remove os
metadados. Um arquivo menor não é garantido: compare os tamanhos antes de adotar o resultado. Definir
a opção PNG quality ativaria a quantização de paleta e poderia perder cores; consulte as
opções de saída do sharp.
Uma nova execução falha se docs/images/v1 já existir, mantendo essa versão intacta. Uma entrada
corrompida também encerra a execução com erro e pode deixar um novo diretório parcialmente gravado.
Não publique esse diretório. Depois de corrigir a entrada, remova apenas o diretório de saída que
falhou e não foi publicado antes de tentar novamente.
Faça commit da imagem gerada
Verifique se docs/images/v1/map.png é o PNG real, não um ponteiro do Git LFS. Faça commit do próprio
arquivo gerado para que o jsDelivr possa obtê-lo. Na raiz do repositório, sem alterações não
relacionadas no stage:
git add docs/images/v1/map.png &&
git commit -m "Add versioned map screenshot" &&
git push origin main &&
git rev-parse HEAD
Guarde o hash completo do commit exibido pelo último comando. A seguir, COMMIT-SHA significa esse
hash, do commit que contém o PNG. Mantenha também optimize.cjs, package.json e yarn.lock junto
com o código-fonte do projeto; node_modules/ não deve entrar no commit.
Use a URL do jsDelivr fixada no commit
A URL da sua imagem é:
https://cdn.jsdelivr.net/gh/YOUR-USERNAME/map-demo@COMMIT-SHA/docs/images/v1/map.png
Abra-a em um navegador depois de substituir os placeholders. Ela deve exibir o PNG gerado. Não é necessário ter conta no jsDelivr nem fazer deploy no Pages. Este é o formato de URL do GitHub documentado.
Use um hash de commit completo em vez de main, latest ou de uma versão omitida. O jsDelivr
armazena em cache permanentemente versões estáticas e URLs de commit
e atribui a elas cabeçalhos de cache de longa duração. Publique imagens alteradas em um novo commit e
atualize a URL; excluir o original do GitHub não remove de forma confiável uma cópia em cache.
Adicione o GitHub Pages como fallback opcional
Você pode usar a URL do jsDelivr no seu próprio site imediatamente. Para dar a este exemplo um
segundo caminho de entrega, publique docs/ pelo Pages. Salve 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>
Confiabilidade e fallback
Salve o seguinte como docs/image.js. Substitua YOUR-USERNAME e COMMIT-SHA, e substitua
map-demo se o seu repositório tiver outro nome. Use o hash do commit da imagem da etapa anterior;
a página e o script podem ser commitados depois.
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',
)
O handler muda para o Pages uma única vez quando ocorre um erro na imagem. Se as duas origens falharem, ele mantém o texto alternativo da imagem disponível e para de tentar novamente. Ele não tem timeout para uma requisição que continua aguardando, e os dois caminhos ainda compartilham o GitHub como fonte. Este é um fallback limitado, não uma garantia de disponibilidade.
Crie um arquivo docs/.nojekyll vazio para
desativar o processamento do Jekyll
e depois publique a página e o 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
Na seção Settings do repositório, abra
Pages. Em Build and deployment,
defina Source como Deploy from a branch,
escolha main e /docs e clique em Save. O
guia de publicação do GitHub
descreve esses controles e a execução de deploy que você deve verificar se a publicação falhar. A
publicação a partir de uma branch usa um fluxo de trabalho do Actions gerenciado pelo GitHub; você
não precisa de um fluxo de trabalho de otimização personalizado.
Aguarde um deploy bem-sucedido antes de abrir https://YOUR-USERNAME.github.io/map-demo/.
Isso pressupõe o domínio padrão de site de projeto, sem domínio personalizado. Os caminhos diferem
porque o Pages publica o conteúdo de docs/, enquanto o jsDelivr lê a partir da raiz do
repositório:
| Local | Caminho da imagem |
|---|---|
| Arquivo commitado | docs/images/v1/map.png |
jsDelivr, após @COMMIT-SHA/ | docs/images/v1/map.png |
Pages, após /map-demo/ | images/v1/map.png |
Para uma atualização, altere o diretório de saída do otimizador para docs/images/v2, gere e inspecione
a nova imagem e faça commit dela. Use o novo hash de commit e o caminho v2 na URL da CDN do
script, e v2 na URL do Pages. Faça deploy do novo arquivo antes de migrar os consumidores.
Mantenha v1 para links antigos. O Pages não fixa uma URL em um commit do Git: o diretório de
versão só permanece estável se você mantiver o conteúdo dele inalterado.
Teste da configuração
Primeiro, abra as duas URLs da imagem diretamente. Um 404 geralmente significa que o arquivo não foi commitado na revisão fixada, que o caminho ou o uso de maiúsculas e minúsculas é diferente, ou que o Pages ainda não fez o deploy da pasta selecionada. Verifique se cada resposta é um PNG, não uma página de erro HTML.
Na página de demonstração, use o inspetor de rede do navegador para confirmar a requisição da imagem e as dimensões dela. Bloqueie a URL exata da imagem no jsDelivr e recarregue: a requisição ao Pages deve ser bem-sucedida. Em seguida, bloqueie as duas URLs e recarregue: deve haver uma tentativa em cada uma, com o texto alternativo da imagem ainda presente. Depois, desbloqueie-as. Teste o comportamento de carregamento da imagem, não apenas uma resposta bem-sucedida da página.
Segurança
Cabeçalhos CORS
Um <img> comum de outra origem pode ser exibido sem ativar o CORS. Ler os pixels dele por meio
de um canvas tem requisitos adicionais de CORS.
Esta demonstração apenas exibe a imagem. CORS não é autenticação e não restringe quem pode baixar um
arquivo público.
Impedir o hotlinking
O JavaScript da sua página não consegue impedir que outra pessoa incorpore a URL pública da imagem. Mantenha imagens privadas fora deste fluxo de trabalho. Para controle de acesso, use um armazenamento e um serviço de entrega capazes de impor autorização ou URLs assinadas.
Quando você precisa de mais de um tamanho de imagem
Este exemplo publica um único PNG nas dimensões originais. Adicionar srcset ou um elemento
<picture> não gera imagens menores nem arquivos AVIF/WebP: esses arquivos precisam ser produzidos
e commitados antes. Um fallback responsivo também precisa limpar os candidatos srcset e <source>
que falharam antes de alterar src; por isso, o handler simples acima é intencionalmente
voltado para um único <img> sem esses candidatos.
Para tamanhos ou formatos dinâmicos, considere um serviço de transformação de imagens, como a
API de processamento de imagens da Transloadit.
