Comparación de optimizadores de PNG en Node.js
Optimizar las imágenes PNG es un paso fundamental para mejorar el rendimiento web. Dado que los
archivos de imagen grandes pueden ralentizar los sitios web, los desarrolladores cuentan con varias
herramientas para comprimir PNG de forma eficaz. En este artículo comparamos tres herramientas
populares de optimización de PNG que se integran bien con Node.js: pngquant,
OptiPNG y sharp.
Descripción general de las herramientas
Pngquant
pngquant es una utilidad de línea de comandos que aplica compresión con pérdida
a las imágenes PNG. Puede reducir significativamente el tamaño de los archivos manteniendo una
calidad visual casi idéntica a la original. Disponible para Linux, macOS y Windows, es una de las
favoritas en muchos pipelines de procesamiento de imágenes.
Optipng
OptiPNG es un optimizador sin pérdida para archivos PNG. Recomprime los datos de la imagen sin
ningún deterioro de la calidad. Aunque quizá no logre la reducción agresiva del tamaño de archivo
que se consigue con pngquant, es ideal para escenarios en los que conservar
cada píxel importa.
Sharp
Sharp es una biblioteca de Node.js de alto rendimiento para el procesamiento de imágenes, incluida
la optimización de PNG. En lugar de invocar herramientas externas de línea de comandos,
sharp se ejecuta directamente en tu entorno de Node.js y ofrece una API
unificada para recortar, redimensionar y comprimir imágenes.
Instalación
Usa Node.js 22.12 o una versión posterior. Instala OptiPNG con el gestor de paquetes de tu
plataforma y los paquetes de Node.js que aparecen a continuación. pngquant-bin
incluye su propio ejecutable de pngquant, cuya versión puede diferir de la instalación de tu
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
Integrar herramientas en tu flujo de trabajo de Node.js
Coloca los siguientes bloques de JavaScript en un solo archivo .mjs.
Comparten las importaciones y las funciones auxiliares. Usa rutas de entrada y de salida distintas:
estos ejemplos sobrescriben los archivos de salida indicados en ejecuciones repetidas, así que elige
rutas dedicadas a las imágenes generadas.
Usar Pngquant mediante procesos hijos
Usa execFile de Node.js con pngquant-bin para un mejor manejo
de errores y compatibilidad 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)
},
)
})
}
La configuración de calidad de pngquant puede provocar el código de salida 99 cuando no se puede alcanzar la calidad mínima. En ese caso, la función devuelve la ruta de entrada original; cualquier salida de una ejecución anterior no se incluye en la comparación de esta ejecución.
Usar Optipng mediante procesos hijos
Implementa OptiPNG con un manejo de errores adecuado y seguimiento del rendimiento:
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 })
}
}
Usar sharp para optimizar dentro del proceso
Este ejemplo de Sharp produce un PNG con paleta y compresión con pérdida, comparable a pngquant.
Para compresión sin pérdida, omite quality, palette y
effort, y conserva compressionLevel: 9. Compara los resultados con
tus propias imágenes; la optimización no garantiza un archivo más pequeño:
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 })
}
}
Comparación del tamaño de los archivos
Usa esta robusta utilidad de comparación para evaluar los resultados de la optimización:
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
})
Mejores prácticas de optimización
Cuando implementes la optimización de imágenes en tu flujo de trabajo:
- Usa worker threads para procesar por lotes grandes cantidades de imágenes
- Implementa un manejo de errores adecuado y reintentos
- Monitorea el uso de memoria al procesar imágenes grandes
- Considera implementar la carga progresiva para la entrega web
- Evalúa WebP como alternativa moderna a PNG
- Implementa una limpieza adecuada de los archivos temporales
- Añade registro y monitoreo para los entornos de producción
Elegir la herramienta adecuada
Cada optimizador ofrece ventajas distintas:
pngquant: la mejor opción para una compresión con pérdida agresiva con un impacto visual mínimo- OptiPNG: ideal para la optimización sin pérdida cuando preservar la calidad es crucial
sharp: excelente para flujos de trabajo integrados de procesamiento de imágenes con buen rendimiento
Ten en cuenta factores como los requisitos de calidad de imagen, la velocidad de procesamiento y la complejidad de la integración al elegir una solución para tus proyectos.
Conclusión
La optimización de PNG es crucial para el rendimiento web. Herramientas modernas como
pngquant, OptiPNG y sharp ofrecen opciones potentes
para implementar una optimización de imágenes eficiente en aplicaciones de Node.js. Cada herramienta
aporta beneficios únicos, y probarlas con tus recursos de imagen específicos te ayudará a determinar
la solución óptima para tus necesidades.
Transloadit usa pngquant en su Robot /image/optimize
para garantizar un procesamiento de imágenes eficiente a escala.
