Comparando otimizadores de PNG em Node.js
Otimizar imagens PNG é uma etapa essencial para melhorar o desempenho na web. Como arquivos de
imagem grandes podem deixar sites mais lentos, os desenvolvedores têm várias ferramentas à
disposição para comprimir PNGs de forma eficaz. Neste post, comparamos três ferramentas populares
de otimização de PNG que se integram bem ao Node.js: pngquant, OptiPNG e
sharp.
Visão geral das ferramentas
Pngquant
pngquant é um utilitário de linha de comando que aplica compressão com perdas
a imagens PNG. Ele consegue reduzir significativamente o tamanho dos arquivos mantendo uma qualidade
visual quase idêntica à original. Disponível para Linux, macOS e Windows, é um dos favoritos em
muitos pipelines de processamento de imagens.
Optipng
O OptiPNG é um otimizador sem perdas para arquivos PNG. Ele recomprime os dados da imagem sem
nenhuma perda de qualidade. Embora talvez não alcance a redução agressiva de tamanho vista com o
pngquant, ele é ideal para cenários em que preservar cada pixel é importante.
Sharp
O Sharp é uma biblioteca Node.js de alto desempenho para processamento de imagens, incluindo
otimização de PNG. Em vez de chamar ferramentas externas de linha de comando, o
sharp roda diretamente no seu ambiente Node.js, oferecendo uma API
unificada para recortar, redimensionar e comprimir imagens.
Instalação
Use o Node.js 22.12 ou posterior. Instale o OptiPNG com o gerenciador de pacotes da sua plataforma e
os pacotes Node.js abaixo. O pngquant-bin fornece seu próprio executável do
pngquant, cuja versão pode ser diferente da instalada no seu sistema:
# Ubuntu/Debian
apt-get install pngquant optipng
# macOS
brew install pngquant optipng
# Node.js
npm install sharp@0.35 pngquant-bin@9 pretty-bytes@7
Integrando as ferramentas ao seu fluxo de trabalho com Node.js
Coloque os blocos de JavaScript a seguir em um único arquivo .mjs. Eles
compartilham imports e funções auxiliares. Use caminhos de entrada e de saída distintos: estes
exemplos sobrescrevem os arquivos de saída nomeados em execuções repetidas, então escolha caminhos
dedicados a imagens geradas.
Usando o Pngquant via processos filhos
Use o execFile do Node.js com pngquant-bin para ter melhor
tratamento de erros e suporte multiplataforma:
import { execFile } from 'node:child_process'
import pngquant from 'pngquant-bin'
function optimizeWithPngquant(inputFile, outputFile) {
console.time('pngquant-optimization')
return new Promise((resolve, reject) => {
execFile(
pngquant,
['--quality=65-80', '--strip', '--speed=3', '--force', '--output', outputFile, '--', inputFile],
(error) => {
console.timeEnd('pngquant-optimization')
if (error?.code === 99) {
// pngquant writes no output when it cannot meet the minimum quality.
resolve(inputFile)
return
}
if (error) {
reject(new Error('Pngquant optimization failed', { cause: error }))
return
}
resolve(outputFile)
},
)
})
}
A configuração de qualidade do pngquant pode gerar o código de saída 99 quando a qualidade mínima não pode ser atingida. Nesse caso, a função retorna o caminho de entrada original; qualquer saída de uma execução anterior não entra na comparação desta execução.
Usando o Optipng via processos filhos
Implemente o OptiPNG com tratamento de erros adequado e acompanhamento de desempenho:
import { promisify } from 'node:util'
const execFilePromise = promisify(execFile)
async function optimizeWithOptipng(inputFile, outputFile) {
console.time('optipng-optimization')
try {
await execFilePromise('optipng', [
'-o7', '-clobber', '-strip', 'all', '-out', outputFile, '--', inputFile,
])
console.timeEnd('optipng-optimization')
return outputFile
} catch (error) {
console.timeEnd('optipng-optimization')
throw new Error('OptiPNG optimization failed', { cause: error })
}
}
Usando o sharp para otimização dentro do processo
Este exemplo com o Sharp gera um PNG com paleta e compressão com perdas, comparável ao pngquant.
Para compressão sem perdas, omita quality, palette e
effort e mantenha compressionLevel: 9. Compare os resultados com
as suas próprias imagens; a otimização não garante um arquivo menor:
import sharp from 'sharp'
async function optimizeWithSharp(inputFile, outputFile) {
console.time('sharp-optimization')
try {
await sharp(inputFile)
.png({
quality: 80,
compressionLevel: 9,
palette: true,
effort: 10,
})
.toFile(outputFile)
console.timeEnd('sharp-optimization')
return outputFile
} catch (error) {
console.timeEnd('sharp-optimization')
throw new Error('Sharp optimization failed', { cause: error })
}
}
Comparação de tamanho de arquivo
Use este utilitário de comparação robusto para avaliar os resultados da otimização:
import fs from 'node:fs'
import prettyBytes from 'pretty-bytes'
function compareFiles(files) {
const results = files.map((file) => {
try {
const stats = fs.statSync(file)
return {
file: file,
size: stats.size,
prettySize: prettyBytes(stats.size),
}
} catch (error) {
return {
file: file,
error: `Could not read file: ${error.message}`,
}
}
})
return results.sort((a, b) => (a.size ?? Infinity) - (b.size ?? Infinity))
}
async function compareOptimizations(inputFile) {
const results = await Promise.all([
optimizeWithPngquant(inputFile, 'output-pngquant.png'),
optimizeWithOptipng(inputFile, 'output-optipng.png'),
optimizeWithSharp(inputFile, 'output-sharp.png'),
])
const comparison = compareFiles([...new Set([inputFile, ...results])])
console.table(comparison)
}
compareOptimizations('input.png').catch((error) => {
console.error(error.message)
process.exitCode = 1
})
Boas práticas de otimização
Ao implementar a otimização de imagens no seu fluxo de trabalho:
- Use worker threads para processar grandes quantidades de imagens em lote
- Implemente tratamento de erros e novas tentativas adequados
- Monitore o uso de memória ao processar imagens grandes
- Considere implementar carregamento progressivo para entrega na web
- Avalie o WebP como uma alternativa moderna ao PNG
- Implemente a limpeza adequada de arquivos temporários
- Adicione logs e monitoramento para ambientes de produção
Escolhendo a ferramenta certa
Cada otimizador oferece vantagens distintas:
pngquant: melhor para compressão com perdas agressiva com impacto visual mínimo- OptiPNG: ideal para otimização sem perdas quando preservar a qualidade é crucial
sharp: excelente para fluxos de trabalho integrados de processamento de imagens com bom desempenho
Considere fatores como requisitos de qualidade de imagem, velocidade de processamento e complexidade de integração ao escolher uma solução para os seus projetos.
Conclusão
A otimização de PNG é crucial para o desempenho na web. Ferramentas modernas como
pngquant, OptiPNG e sharp oferecem opções poderosas
para implementar uma otimização de imagens eficiente em aplicações Node.js. Cada ferramenta oferece
benefícios únicos, e testá-las com as suas próprias imagens ajudará a determinar a solução ideal
para as suas necessidades.
A Transloadit usa o pngquant no Robot
/image/optimize para garantir um processamento de imagens eficiente
em escala.
