Gerador de Data URI

Codifique uma imagem, um tipo de letra ou texto como data: URI, ou descodifique um: Base64 e codificação percentual medidos lado a lado. Corre no navegador.

Origem
ou largue um aqui — é lido localmente e nunca enviado
Resultado
data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%232563eb'%20stroke-width='2'%3E%3Ccircle%20cx='12'%20cy='12'%20r='9'/%3E%3Cpath%20d='M8%2012.5l2.5%202.5%205-6'/%3E%3C/svg%3E

Aqui a codificação percentual é mais curta: 255 caracteres contra 272, poupando 17.

Tamanhos

Bytes de origem
174
URI, em Base64
272
URI, com codificação percentual
255

Um ficheiro dentro de um endereço

Um data URI é um ficheiro inteiro escrito como um endereço. Em vez de apontar para um recurso que o navegador tem de ir buscar, transporta os próprios bytes, de modo que um ícone pode viver dentro da folha de estilos que o usa e uma imagem pequena dentro do HTML que a mostra. O RFC 2397 definiu o esquema em 1998 e há duas décadas que é suportado em toda a parte.

A gramática é curta. Depois do esquema vem um tipo de media opcional com parâmetros opcionais, depois uma marca base64 opcional, depois uma vírgula, depois os dados. Tudo o que está antes da vírgula é o cabeçalho e tudo o que vem depois são os dados — é por isso que o navegador divide na primeira vírgula e por isso que uma vírgula dentro dos dados é inofensiva.

data:[<mediatype>][;base64],<data>

data:,hello                          text/plain;charset=US-ASCII
data:text/plain;charset=utf-8,hello  the same bytes, spelled out
data:image/png;base64,iVBORw0KGgo=   binary, Base64 encoded
data:image/svg+xml,%3Csvg%20...      markup, percent-encoded

Um cabeçalho vazio é válido e significa text/plain;charset=US-ASCII, o único valor por omissão que a especificação lhe entrega. Esta página avisa que o aplicou em vez de mostrar um tipo de media que nunca escreveu.

Duas codificações, e qual é mais curta

O RFC define duas maneiras de escrever os dados, e a escolha não é uma questão de gosto: para um ficheiro concreto uma delas é claramente mais curta, e qual seja depende inteiramente do que o ficheiro contém.

O Base64 reescreve cada três bytes como quatro caracteres de um alfabeto de 64. O custo é fixo e previsível: exactamente um terço mais, além do preenchimento. Funciona com qualquer coisa, e por isso é a forma a que se recorre por hábito.

A codificação percentual deixa tal e qual qualquer carácter válido num URL e gasta três caracteres em todos os outros. Com dados que são sobretudo ASCII simples — um SVG, uma folha de estilos pequena, um bloco de JSON — a maioria dos bytes passa intacta e o resultado sai mais curto do que em Base64, muitas vezes vinte a trinta por cento. Com um PNG ou um tipo de letra, onde quase todos os bytes têm de ser escapados, aproxima-se do triplo do original e fica muito pior do que o Base64.

Quanto se escapa é, em si, uma decisão, e esta página oferece as duas respostas:

  • O conjunto rigoroso escapa todos os bytes fora dos caracteres não reservados do RFC 3986: letras, dígitos, hífen, ponto, traço inferior e til. O resultado é seguro em qualquer contexto, ao preço de escapar pontuação que não precisava.
  • O conjunto mínimo mantém tudo o que a gramática do data URI realmente permite, incluindo as barras, os dois pontos, os sinais de igual, as aspas e os parênteses de que a marcação está cheia. É aqui que a codificação percentual ganha, e é por isso que um SVG escrito com plicas à volta dos atributos codifica muito melhor do que um escrito com aspas duplas.
  • O destino acrescenta mais uma regra. Dentro de um atributo HTML um E comercial isolado inicia uma referência de carácter, portanto escolher o destino img também o escapa. Nada mais muda: as aspas duplas, os sinais de menor e maior e a barra invertida já estão fora do conjunto permitido e são escapados nos dois níveis.
  • O sinal de percentagem é sempre escapado, em qualquer nível. Ele abre uma sequência de escape, por isso uma percentagem literal tem de se escrever %25 ou os dois caracteres seguintes são lidos como hexadecimais.

Em vez de o deixar adivinhar, a página codifica os dados das duas maneiras e indica os dois comprimentos e a diferença. Escolha o vencedor, ou fixe a codificação à mão se tiver um motivo.

Os 33% são um número anterior à compressão

Quase toda a discussão sobre data URIs repete que o Base64 acrescenta um terço ao tamanho do ficheiro. Aritmeticamente é verdade e na prática é enganador, porque o número descreve os bytes antes de chegarem à rede, e qualquer servidor comprime texto à saída.

O Base64 não acrescenta informação; espalha a mesma informação por mais caracteres, usando apenas 64 dos 256 valores que um byte pode tomar. É exactamente o tipo de redundância que o gzip e o Brotli existem para remover. Com dados já comprimidos — um PNG, um JPEG, um tipo de letra WOFF2 — o compressor não consegue tocar no conteúdo, mas consegue voltar a arrumar o Base64 até algo muito próximo do tamanho original. O famoso terço desaparece quase todo.

Por isso esta página mede em vez de afirmar. Comprime três coisas com a própria implementação de gzip do navegador: os dados tal como seriam servidos num ficheiro separado, o documento que os leva em Base64 e o documento que os leva com codificação percentual. Esses três números são a versão honesta do argumento do tamanho, e mostram muitas vezes que o verdadeiro custo de incorporar não é o tamanho de todo.

Os custos verdadeiros estão noutro lado. Um ficheiro incorporado não pode ser guardado em cache por si só, portanto é descarregado outra vez com cada cópia do documento que o leva e nunca será partilhado entre duas páginas. Além disso bloqueia: uma folha de estilos não acaba de ser analisada enquanto não tiver lido por inteiro o URI que está no meio dela. O HTTP/2 eliminou quase toda a penalização por pedido que tornava atraente incorporar. Para um ícone minúsculo que aparece em todas as páginas continua a ser uma troca razoável; acima de algumas dezenas de kilobytes normalmente não, e é por isso que esta página começa a avisar aos trinta.

O tipo vem dos bytes

Um data URI só serve se o tipo de media estiver certo. Erre-o e o navegador recusa-se a mostrar a imagem, aplica o analisador errado ou — para tudo o que seja servido com um tipo que não reconhece — oferece antes uma transferência.

Quando larga um ficheiro, o navegador comunica um tipo próprio, mas essa comunicação deriva da extensão do ficheiro e de mais nada. Mude o nome de um JPEG para .png e o navegador chamar-lhe-á PNG. Esta página lê antes os primeiros bytes. Quase todos os formatos binários começam por uma assinatura: o PNG começa por um byte que não pode aparecer em ASCII seguido das letras PNG, o JPEG por três bytes fixos, o PDF por um sinal de percentagem e a palavra PDF, e o WOFF e o WOFF2 pelas suas etiquetas de quatro caracteres. Quando a assinatura e a extensão não coincidem, ganha a assinatura e a discrepância é assinalada.

A lista é deliberadamente curta: os formatos que as pessoas incorporam mesmo, não tudo o que um navegador consegue farejar. Se nada coincidir e os bytes forem texto válido, o tipo é texto, distinguindo SVG de XML pelo que a marcação abre. Se não coincidir absolutamente nada, a resposta é application/octet-stream, e o campo continua editável, porque um palpite errado é pior do que um assumido.

Para dados textuais também importa o conjunto de caracteres. O valor por omissão da especificação é US-ASCII, que não é o que hoje alguém quer dizer, por isso um tipo de media textual é oferecido com charset=utf-8 acrescentado. Omiti-lo não corrompe os bytes, mas muda a forma como são lidos de volta.

Ler um de volta

A outra metade do trabalho aparece mais vezes: encontrou um data: URI numa folha de estilos, num despejo do DOM ou numa página guardada e quer saber o que é. Cole-o e a página desmonta-o — o tipo de media, os seus parâmetros, que codificação foi usada, para quantos bytes descodifica contra quantos caracteres custa, e o que são esses bytes independentemente do que o cabeçalho afirma. Uma imagem é desenhada, o texto é mostrado, e tudo o resto recebe os seus primeiros bytes em hexadecimal.

A descodificação é rigorosa, porque um URI colado é entrada não fiável e uma resposta plausível mas errada é pior do que um erro. Uma vírgula em falta, um tipo de media malformado, um sinal de percentagem sem dois dígitos hexadecimais a seguir, um carácter fora do alfabeto Base64: cada um falha indicando a posição do problema em vez de ser reparado em silêncio.

Duas coisas são toleradas, porque qualquer navegador as tolera e porque um URI copiado de uma folha de estilos com mudanças de linha seria de outro modo inutilizável. Os espaços dentro dos dados são removidos e o preenchimento Base64 em falta é completado. Ambas são comunicadas, para que saiba que o URI que tem não é exactamente o URI que funciona em toda a parte.

Uma coisa parecida é recusada logo à partida. O alfabeto Base64 seguro para URL, que troca o mais e a barra pelo hífen e pelo traço inferior, é o que os JSON Web Token usam — e não é o que um data URI aceita. Aceitá-lo em silêncio significaria descodificar para bytes diferentes dos do navegador, por isso é recusado pelo nome, com a posição do carácter em falta.

Onde corre

Tudo acontece no seu navegador. O ficheiro que escolhe é lido com a API de ficheiros local e nunca é enviado; a codificação, a medição da compressão e a descodificação correm todas na sua máquina, e nada é guardado ou registado. Isso importa aqui mais do que na maioria das páginas, porque os ficheiros que as pessoas transformam em data URI são muitas vezes recursos internos, e os URIs que colam para inspeccionar vêm de páginas em produção.

Perguntas frequentes

O meu ficheiro é enviado para algum lado?
Não. O ficheiro é lido localmente com a própria API de ficheiros do navegador, codificado na sua máquina e não é enviado para lado nenhum. O mesmo vale para um URI que cole para descodificar.
Devo usar Base64 ou codificação percentual?
A que for mais curta para os seus dados, que é o que a página mede por si. Como regra prática: codificação percentual para SVG, CSS, JSON e restante texto, Base64 para imagens, tipos de letra e tudo o que já esteja comprimido. A diferença é muitas vezes de vinte a trinta por cento num sentido ou no outro.
O Base64 aumenta mesmo um ficheiro em 33%?
Antes da compressão, sim: quatro caracteres por cada três bytes. Na rede, na maior parte não. O Base64 usa apenas 64 dos 256 valores possíveis de um byte, e essa redundância é exactamente o que o gzip remove, por isso o tamanho comprimido fica normalmente perto do tamanho comprimido do ficheiro original. A página mede os dois para que o veja.
Qual é o tamanho máximo de um data URI?
Os navegadores modernos não impõem um limite rígido a um URI dentro de um documento, embora versões antigas do Internet Explorer o limitassem a 32 KB. O tamanho é uma questão de desempenho e não de legalidade: um ficheiro incorporado não pode ser guardado em cache separadamente e é descarregado de novo com cada cópia do documento. Esta página codifica até um megabyte e avisa acima de trinta kilobytes.
Porque é que o meu data URI com SVG se parte no CSS?
Quase sempre por um carácter não escapado. Um cardinal — de uma cor como #2563eb — inicia um identificador de fragmento e corta o URI nesse ponto. Umas aspas duplas fecham a cadeia de CSS em que estão. Escreva o SVG com plicas à volta dos atributos e use o conjunto de escape mínimo, que escapa o cardinal e as aspas duplas e deixa curto tudo o resto.
Posso usar um data URI para um script ou um iframe?
Pode, mas trate isso como uma questão de segurança e não de conveniência. Um data URI não herda origem, e os navegadores já bloqueiam a navegação de nível superior para um deles precisamente por isso. Uma política de segurança de conteúdo que permita data: em script-src ou frame-src abdica de quase tudo o que a política protegia; permiti-lo em img-src ou font-src é corrente.
Porque foi recusado o meu Base64 seguro para URL?
Porque um data URI aceita Base64 padrão. O alfabeto seguro para URL troca o mais e a barra pelo hífen e pelo traço inferior, que são caracteres diferentes e se descodificam para bytes diferentes. Aceitá-lo significaria que esta página descodificava o seu URI de maneira diferente do navegador que o vai realmente carregar.
O navegador diz que o meu ficheiro é de um tipo e esta página diz outro. Qual está certo?
A página. Um navegador comunica o tipo que infere da extensão do ficheiro, por isso engana-se com um ficheiro cujo nome foi mudado. Esta página lê a assinatura no início do ficheiro, que é o que o próprio formato declara. O campo continua editável se tiver um motivo para o substituir.