Guías

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.

Estándares y referencias del navegador