Guias

Como comprimir uma imagem Base64 (por que Gzip costuma falhar)

Aprenda a reduzir Base64 otimizando a imagem decodificada, por que Gzip não substitui Data URL e como buscar 100 KB com JavaScript.

Por que Base64 fica cerca de 33,3% maior

Base64 codifica três bytes em quatro caracteres. O comprimento exato é 4 × ceil(n / 3); o prefixo Data URL vem além disso.

Três fluxos possíveis

Bytes→Base64 é padrão, mas maior. Base64→Gzip→Base64 exige desfazer duas camadas. Decodificar imagem→otimizar→Base64 mantém uma imagem comum para img src.

Por que Gzip perde ganho ao voltar a texto

O Gzip intermediário pode parecer menor, mas outro Base64 e cabeçalhos adicionam custo. O resultado é Gzip, não JPEG, PNG ou WebP, e não abre diretamente.

Formatos de imagem já são comprimidos

JPEG, PNG e WebP usam compressão própria. A economia real costuma vir de menos pixels, qualidade com perda, metadados removidos ou formato adequado.

JavaScript correto no navegador

Valide Base64 e magic bytes, decodifique com ImageBitmap, desenhe em Canvas limitado e use toBlob. Em produção, limite tamanho, rejeite animação, detecte WebP e descarte tarefas antigas.

Exemplo curto com APIs 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();
  }
}

Como chegar a 100 KB

Trate 100 KB como 102.400 bytes. Busque qualidade JPEG/WebP com limite; se necessário, redimensione com sqrt(alvo/atual) em poucas rodadas.

Qualidade, dimensões e formato

Qualidade altera o codec; redimensionar remove pixels. Fotos combinam com JPEG/WebP e transparência com PNG/WebP. Compare a saída real.

Transparência PNG e fundo JPEG

JPEG não tem alfa. Mantenha PNG/WebP ou exija fundo confirmado antes de achatar.

GIF, WebP animado e SVG

Canvas normalmente captura um quadro. Recuse GIF, WebP animado e ICO; SVG precisa de otimização textual própria.

Local melhora privacidade, não prova segurança

Evita upload e retenção, mas não substitui antivírus. Dispositivo, extensões e codecs continuam relevantes.

Quando abandonar Base64

Para imagens grandes, use Blob URL, arquivo separado com cache ou upload binário via Blob, FormData ou ArrayBuffer.

Perguntas frequentes

Base64 pode ser comprimido?

Sim, mas compressão genérica não gera Base64 de imagem diretamente utilizável.

Funciona em img src?

Só se o conteúdo decodificado continuar sendo imagem; Gzip precisa ser aberto.

Como chegar a 100 KB?

Busque qualidade com limite e depois reduza dimensões proporcionalmente.

Perde qualidade?

Redimensionar e usar qualidade com perda pode reduzir a qualidade.

Por que 33% maior?

Três bytes são representados por quatro caracteres.

É enviado ao servidor?

O compressor trabalha somente no navegador.

Gzip antes ou depois?

Use compressão HTTP ao redor do documento, não dentro da Data URL.

Padrões e referências do navegador