Cómo comprimir una imagen Base64 (por qué Gzip suele fallar)
Aprende a reducir Base64 optimizando la imagen decodificada, por qué Gzip no sustituye una Data URL y cómo buscar 100 KB con JavaScript.
Por qué Base64 aumenta cerca del 33,3%
Base64 codifica tres bytes como cuatro caracteres. La longitud exacta es 4 × ceil(n / 3); el prefijo de Data URL se suma aparte.
Tres cadenas de procesamiento
Bytes→Base64 es estándar pero mayor. Base64→Gzip→Base64 exige deshacer dos capas. Decodificar imagen→optimizar→Base64 conserva una imagen normal para img src.
Por qué Gzip pierde ventaja al volver a texto
El binario Gzip puede parecer menor, pero otro Base64 y las cabeceras añaden coste. Además es Gzip, no JPEG, PNG o WebP, y no se muestra directamente.
Los formatos ya están comprimidos
JPEG, PNG y WebP usan compresión propia. El ahorro real suele venir de menos píxeles, calidad con pérdida, metadatos eliminados o un formato mejor.
JavaScript correcto en el navegador
Valida Base64 y magic bytes, decodifica con ImageBitmap, dibuja en Canvas limitado y recodifica con toBlob. En producción añade límites, rechazo de animación, detección WebP y control de tareas antiguas.
Ejemplo breve con API nativas
async function compressBase64Image(dataUrl, maxWidth = 1600, quality = 0.82) {
const [header, payload] = dataUrl.split(',', 2);
const mime = header.match(/^data:([^;]+);base64$/)?.[1];
if (!mime || !/^image\/(png|jpeg|webp)$/.test(mime)) throw new Error('Unsupported image');
const binary = atob(payload.replace(/\s+/g, ''));
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
const bitmap = await createImageBitmap(new Blob([bytes], { type: mime }));
try {
const scale = Math.min(1, maxWidth / bitmap.width);
const canvas = document.createElement('canvas');
canvas.width = Math.max(1, Math.round(bitmap.width * scale));
canvas.height = Math.max(1, Math.round(bitmap.height * scale));
const context = canvas.getContext('2d');
if (!context) throw new Error('Canvas 2D unavailable');
context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/webp', quality));
if (!blob) throw new Error('Encoding failed');
const output = new Uint8Array(await blob.arrayBuffer());
let outputBinary = '';
for (let offset = 0; offset < output.length; offset += 0x8000) {
outputBinary += String.fromCharCode(...output.subarray(offset, offset + 0x8000));
}
return `data:${blob.type};base64,${btoa(outputBinary)}`;
} finally {
bitmap.close();
}
}
Cómo llegar a 100 KB
Usa 102.400 bytes de imagen. Busca calidad JPEG/WebP con un límite; si no basta, reduce proporcionalmente con sqrt(objetivo/actual) durante pocas rondas.
Calidad, dimensiones y formato
La calidad cambia el codec; redimensionar elimina píxeles. Fotos suelen encajar en JPEG/WebP y transparencia en PNG/WebP. Compara resultados reales.
Transparencia PNG y fondo JPEG
JPEG no tiene alfa. Mantén PNG/WebP o exige un fondo confirmado antes de aplanar.
GIF, WebP animado y SVG
Canvas suele capturar un fotograma. Rechaza GIF, WebP animado e ICO; SVG requiere un optimizador de texto específico.
Local mejora privacidad, no demuestra seguridad
Evita subida y retención servidor, pero no sustituye un análisis de malware. Siguen existiendo riesgos del dispositivo, extensiones y codecs.
Cuándo abandonar Base64
Para imágenes grandes usa Blob URL, archivos separados cacheables o subida binaria con Blob, FormData o ArrayBuffer.
Preguntas frecuentes
¿Se puede comprimir Base64?
Sí, pero la compresión general no produce Base64 de imagen directamente utilizable.
¿Funciona en img src?
Solo si lo decodificado sigue siendo una imagen; Gzip debe descomprimirse.
¿Cómo llegar a 100 KB?
Busca calidad con límite y luego reduce dimensiones proporcionalmente.
¿Baja la calidad?
Redimensionar y usar calidad con pérdida puede reducirla.
¿Por qué 33% más?
Tres bytes se representan con cuatro caracteres.
¿Se sube al servidor?
El compresor procesa todo dentro del navegador.
¿Gzip antes o después?
Usa compresión HTTP normal alrededor del documento, no dentro de la Data URL.