Automatizando a otimização de JPEG com jpegoptim em seus projetos
Execute o jpegoptim em cópias das suas imagens em um diretório de build. Assim, você obtém JPEGs otimizados para publicar e mantém intactos os seus originais e a área de preparação do Git. Este passo a passo gera um diretório de JPEGs otimizados sem perdas no Linux e é interrompido com um erro se alguma imagem falhar.
Instale o jpegoptim no Ubuntu
Você precisa do Bash, do GNU coreutils e findutils e de um diretório existente de assets JPEG. O
script abaixo usa opções do GNU realpath; é um exemplo para Linux, não um script nativo para macOS ou
Windows. Ele foi testado com o pacote jpegoptim 1.4.7 do Ubuntu 24.04 e com o jpegoptim 1.5.6 no
Linux.
No Ubuntu 24.04, instale o pacote jpegoptim:
sudo apt-get update &&
sudo apt-get install -y jpegoptim &&
jpegoptim --version
O gerenciador de pacotes instala as bibliotecas de runtime. Você não precisa de libjpeg-dev para
executar o programa empacotado.
Pré-visualize uma imagem
Substitua photo.jpg por um arquivo do seu diretório de assets:
jpegoptim --noaction --strip-none --nofix -- assets/images/photo.jpg
--noaction informa o tamanho de um candidato sem gravar o arquivo. Sem --max ou --size,
o jpegoptim otimiza a codificação do JPEG sem perdas. O
manual dele distingue isso
da redução da qualidade da imagem. Os bytes comprimidos podem mudar enquanto os pixels decodificados
continuam iguais. Um resultado skipped pode significar simplesmente que o candidato não ficou menor.
Gere um diretório separado de JPEGs otimizados
Salve isto como optimize-jpegs.sh na raiz do seu projeto. O script copia arquivos regulares .jpg e
.jpeg, incluindo extensões em maiúsculas e diretórios aninhados, e depois otimiza as cópias.
Outros arquivos e entradas de links simbólicos são excluídos. Não altere a árvore de origem enquanto
o script é executado.
#!/usr/bin/env bash
set -euo pipefail
if (( $# != 2 )); then
printf 'Usage: bash optimize-jpegs.sh SOURCE NEW_OUTPUT\n' >&2
exit 1
fi
command -v jpegoptim >/dev/null || {
printf 'Install jpegoptim first.\n' >&2
exit 1
}
IFS= read -r -d '' source_dir < <(realpath -e -z -- "$1")
[[ -d "$source_dir" ]] || { printf 'Source must be a directory.\n' >&2; exit 1; }
IFS= read -r -d '' output_dir < <(realpath -m -z -- "$2")
if [[ "$source_dir" == / || "$output_dir/" == "$source_dir/"* ]]; then
printf 'Output must be outside the source tree.\n' >&2
exit 1
fi
if [[ -e "$2" || -L "$2" ]]; then
printf 'Output already exists; choose a new directory.\n' >&2
exit 1
fi
mkdir -- "$output_dir"
find "$source_dir" -type f \( -iname '*.jpg' -o -iname '*.jpeg' \) -print0 |
while IFS= read -r -d '' file; do
relative=${file#"$source_dir"/}
target="$output_dir/$relative"
mkdir -p -- "${target%/*}"
cp -- "$file" "$target"
jpegoptim --strip-none --nofix -- "$target"
done
printf 'JPEG build ready: %s\n' "$output_dir"
Execute-o a partir da raiz do projeto, com um diretório de saída que ainda não exista. O diretório pai dele já deve existir. Este exemplo grava ao lado do seu projeto:
bash optimize-jpegs.sh assets/images ../optimized-images
Por exemplo, assets/images/products/front.JPG se torna
../optimized-images/products/front.JPG. Copiar primeiro também mantém os arquivos que não podem ficar
menores: a opção --dest do jpegoptim, sozinha, pode omitir esses arquivos. O script nunca
sobrescreve um diretório de saída existente, então uma segunda execução precisa de um novo destino.
A lista de arquivos delimitada por caracteres nulos e os caminhos entre aspas lidam com espaços,
quebras de linha, hifens iniciais e caracteres % literais nos nomes de arquivos. Tanto a descoberta
quanto a cópia usam o mesmo caminho de origem resolvido, inclusive quando o argumento de diretório
contém um link simbólico seguido de ...
--nofix rejeita imagens que geram avisos de decodificação em vez de tentar repará-las. Uma cópia ou
otimização com falha interrompe o loop, e o pipefail do Bash também propaga uma falha de find.
Uma falha após a criação da saída pode deixar um diretório incompleto; inspecione ou remova esse
build com falha antes de tentar novamente. Publique somente após um status de saída zero e a
mensagem final de sucesso. Um diretório de origem vazio é concluído com sucesso e gera um diretório
de saída vazio.
Mantenha intactas as alterações preparadas do commit
Evite um hook de pre-commit que otimize os arquivos de trabalho e depois execute git add assets/images.
O Git adiciona o estado atual de todo esse diretório, incluindo
edições não relacionadas, arquivos novos e exclusões. Ele também pode substituir uma versão de uma
imagem preparada deliberadamente pela versão diferente que está na sua árvore de trabalho.
Use o script de build como uma tarefa separada. Ele não executa comandos do Git nem reescreve as imagens de origem, então o trabalho preparado, parcialmente preparado, não preparado e não rastreado fica como você deixou. Localmente, ele lê a árvore de trabalho, incluindo JPEGs não rastreados; a saída dele não é um snapshot do seu commit preparado. Use um checkout limpo na CI quando a saída precisar corresponder a um commit.
Execute o mesmo script na CI
Em uma tarefa de CI no Ubuntu, faça o checkout do projeto, execute o comando de instalação acima e
depois chame o script a partir da raiz do projeto. Em uma etapa de shell do GitHub Actions, use um
diretório novo dentro de
RUNNER_TEMP
como segundo argumento. Configure o upload de artefatos ou o deploy para consumir esse diretório
somente depois que o script for concluído com sucesso. O script gera assets JPEG, não um site
completo; seu build ainda precisa fornecer os outros arquivos do site e referenciar os assets
otimizados.
Escolha as políticas de metadados e qualidade de forma deliberada
O script usa --strip-none para manter os marcadores de metadados, incluindo a orientação EXIF, os perfis de
cor ICC e os comentários. Os marcadores JFIF e Adobe ainda podem ser regenerados pela biblioteca
JPEG. Isso não é um arquivamento byte a byte nem uma limpeza de privacidade: informações de
localização e da câmera podem permanecer.
Não use --strip-all como substituto sem considerar como suas imagens usam esses metadados. Remover
informações de orientação ou de cor pode afetar a exibição delas mesmo quando as amostras de pixels
decodificadas não mudam. As opções de metadados
descrevem quais marcadores cada flag mantém ou remove.
Para obter arquivos menores ao custo da qualidade da imagem, --max=80 ativa a otimização com perdas
quando aplicável. É um teto de qualidade, não uma meta de 80% do tamanho. --size também ativa a
otimização com perdas e busca atingir um tamanho, em vez de garanti-lo. Avalie essas mudanças em
cópias novas dos seus originais; o script de build continua sem perdas de propósito.
Meça suas próprias imagens
Compare a contagem de bytes do original e da saída de uma imagem que você gerou:
wc -c -- assets/images/photo.jpg ../optimized-images/photo.jpg
Não existe uma porcentagem fixa de economia. Uma imagem já otimizada pode continuar do mesmo tamanho, e assets menores, por si sós, não comprovam uma melhora no carregamento da página. Verifique a saída real do seu build e meça a página depois que ela passar a servir esses arquivos. Guarde os originais para poder rever as escolhas de qualidade ou de metadados sem partir de uma imagem já recomprimida.
