Codificação Base64 em Kotlin: um guia completo
Metade de toda conversa sobre Base64 é sobre ler dados empacotados de volta para bytes. A outra metade, a parte para a qual você está no site certo, é sobre produzir esse dado empacotado em primeiro lugar. Em algum lugar do seu aplicativo há bytes que precisam viajar por um canal que só entende texto: uma string JSON, um header HTTP, um e-mail, uma URL, um arquivo de configuração. Base64 é a resposta clássica, e o Kotlin tem uma resposta de primeira classe para isso na biblioteca padrão: a classe Base64 em kotlin.io.encoding, estável desde o Kotlin 2.2.
Este guia passeia pelo que você realmente precisa quando você é quem produz Base64: os quatro esquemas prontos, o controle de padding, as regras de quebra de linha que e-mails e certificados obedecem, o alfabeto seguro para URL, de onde vêm os bytes, e um conjunto de cenários do mundo real com Kotlin em cada um. O formato em si, como 3 bytes viram 4 caracteres, de onde vem o =, é coberto na página inicial, então aqui vamos direto ao Kotlin.
Uma classe, quatro presets
A API inteira é uma única classe, Base64, no pacote kotlin.io.encoding. Não existe um objeto encoder para construir e não existe builder. Em vez disso, a classe vem com quatro instâncias prontas, uma por esquema de RFC, e um objeto companion que faz as vezes, em silêncio, da mais comum:
| Instância | Alfabeto | Quebra de linhas na codificação | Padding na codificação | Use para |
|---|---|---|---|---|
Base64.Default | + e / | nenhuma | emite = | uso geral, APIs, data URLs |
Base64.UrlSafe | - e _ | nenhuma | emite = (dá para desligar) | URLs, tokens, JWTs |
Base64.Mime | + e / | CRLF a cada 76 caracteres | emite = | corpos de e-mail e anexos |
Base64.Pem | + e / | CRLF a cada 64 caracteres | emite = | certificados e chaves privadas |
O detalhe de nomenclatura que trava as pessoas: essas são instâncias, não fábricas. Toda instância é um valor imutável, e mudar o comportamento dela, como o padding, retorna uma instância nova em vez de modificar a antiga. Isso torna os presets seguros para compartilhar entre threads e guardar em objetos, e é por isso que a classe inteira pode ser um tipo de valor simples, sem estado interno.
Sua primeira codificação: bytes na entrada, String na saída
Aqui está o menor programa útil do site: cinco bytes entram, uma string de oito caracteres sai. A entrada é sempre um ByteArray (ou um pedaço de um), e o resultado é uma String comum que você pode colocar em qualquer lugar onde texto é permitido:
import kotlin.io.encoding.Base64
fun main() {
val bytes = "Hello".encodeToByteArray()
val packed = Base64.encode(bytes)
println(packed) // SGVsbG8=
}
Essa uma linha faz mais trabalho do que parece. O Kotlin te dá várias formas da mesma operação, e todas leem como a função diz:
encode(bytes)retorna umaString, a forma acima.encodeToByteArray(bytes)retorna umByteArrayde caracteres ASCII, útil quando a forma empacotada vai para outro buffer.encodeIntoByteArray(bytes, destination)escreve numByteArrayque você já alocou, o que pula uma alocação em caminhos quentes.encodeToAppendable(bytes, builder)acrescenta em qualquer coisa que implementeAppendable, como umStringBuilder, que é o encaixe natural quando você está montando um documento maior.
As quatro aceitam o mesmo intervalo opcional de startIndex e endIndex, então você pode empacotar um pedaço de um buffer grande sem copiá-lo antes. Como Base64.Default é o objeto companion, você também pode dispensar a instância e escrever Base64.encode(bytes) como açúcar sintático; as duas formas são a mesma chamada.
A regra 4/3: quanto vai ficar grande?
Antes de mandar um codificador para produção, vale saber exatamente quanto maior a saída vai ficar, porque o Base64 gasta caracteres com informação que ele já tinha. A matemática é rígida: cada 3 bytes de entrada viram exatamente 4 caracteres de saída, então qualquer resto de 1 ou 2 bytes ainda consome um grupo inteiro de 4, completado com = para fechar o grupo. O resultado para os primeiros tamanhos:
| Bytes de entrada | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| Caracteres de saída | 4 | 4 | 4 | 8 | 8 | 8 | 12 | 12 |
A fórmula por trás da tabela é 4 * ceil(bytes / 3). No pior caso um byte vira 4 caracteres, um acréscimo de 300 por cento; a partir de três bytes ela converge para mais ou menos um terço a mais de dados na linha. Esse é o modelo de custo inteiro; não há variação por instância, e é por isso que você deve ser deliberado em empacotar payloads grandes em Base64, em vez de recorrer a ele por reflexo.
Padding é um ajuste, não um destino
No lado da codificação, os caracteres = são uma decisão de política, e o Kotlin a trata como primeira classe. Toda instância carrega um PaddingOption, e withPadding te entrega uma instância nova com o controle girado. Os quatro presets começam em PRESENT, que é por isso que "Hello" sai como SGVsbG8= em vez de SGVsbG8:
import kotlin.io.encoding.Base64
fun main() {
val bytes = "Hello".encodeToByteArray()
val noPad = Base64.Default.withPadding(Base64.PaddingOption.ABSENT)
println(Base64.encode(bytes)) // SGVsbG8=
println(noPad.encode(bytes)) // SGVsbG8
}
Há quatro posições no controle. A primeira palavra do nome decide o que o codificador emite; a segunda metade decide o quão estrito será o decodificador da mesma instância quando você (ou o outro lado) der a volta mais tarde:
| PaddingOption | Codificador emite = | Decodificador aceita = |
|---|---|---|
PRESENT | sim | obrigatório, qualquer outra coisa falha |
ABSENT | não | proibido, um padding solto falha |
PRESENT_OPTIONAL | sim | dos dois jeitos |
ABSENT_OPTIONAL | não | dos dois jeitos |
A escolha mais comum no momento de codificar é ABSENT com o alfabeto UrlSafe, que é exatamente a forma que os JSON Web Tokens e muitos esquemas de URL esperam. Você vai reencontrá-la num instante.
Base64url: URLs, tokens e JWTs
O alfabeto clássico contém + e /, e os dois são desastres em URLs: um + numa query string é lido como espaço com toda regularidade, e um / é o separador de caminho. A seção 5 do RFC 4648 define a variante segura para URL, trocando para - e _, e Base64.UrlSafe é esse esquema. Codificar os mesmos bytes que produzem um / no alfabeto clássico mostra a troca em ação:
import kotlin.io.encoding.Base64
fun main() {
val bytes = "Hello?".encodeToByteArray()
println(Base64.encode(bytes)) // SGVsbG8/
println(Base64.UrlSafe.encode(bytes)) // SGVsbG8_
}
O usuário real de referência é um JWT, cujo header e payload são base64url sem padding, unidos por pontos. Aqui está a metade da codificação de construir um, que é uma forma que você deve entender mesmo que uma biblioteca assine o token final:
import kotlin.io.encoding.Base64
fun main() {
val header = """{"alg":"HS256","typ":"JWT"}"""
val payload = """{"sub":"1234567890","name":"John Doe"}"""
val noPad = Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT)
val h = noPad.encode(header.encodeToByteArray())
val p = noPad.encode(payload.encodeToByteArray())
val token = "$h.$p.Ym9nVXNlZlNpZ25hdHVyZUZvckRlbW8"
println(token)
}
Imprime:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.Ym9nVXNlZlNpZ25hdHVyZUZvckRlbW8
Dois avisos cabem aqui. Primeiro, o terceiro segmento é uma assinatura, e produzir uma genuína precisa de criptografia de verdade (um signador JCA/JCE ou uma biblioteca de JWT), nunca de bytes montados à mão; o trecho acima só está demonstrando a forma da codificação. Segundo, se você está no JVM e pega java.util.Base64.getUrlEncoder() por hábito, note que ele aplica padding por padrão, então saída estilo JWT precisa de .withoutPadding() lá; o preset do Kotlin aplica padding com a mesma intensidade de fábrica, e você opta fora com uma chamada withPadding em vez disso.
Quebra de linhas: os presets Mime e Pem
Dos quatro presets, dois quebram a saída em linhas curtas, e a razão é histórica. Transportes de e-mail antigos amassavam linhas longas, então a seção 6.8 do RFC 2045 limita o base64 MIME a 76 caracteres por linha; as ferramentas de PKI, seguindo a tradição mais antiga do PEM, usam 64. O Kotlin embute as duas regras no próprio preset: o separador de linha é CRLF, a quebra cai exatamente no limite, e não há separador no final. Um payload de 200 bytes passando por cada um parece assim:
import kotlin.io.encoding.Base64
fun main() {
val data = ByteArray(200) { (it % 251).toByte() }
println(Base64.Mime.encode(data).lines().maxOf { it.length }) // 76
println(Base64.Pem.encode(data).lines().maxOf { it.length }) // 64
}
Para essa entrada, o Mime produz 4 linhas e o Pem produz 5. A primeira linha do Mime é:
AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4
A armadilha para lembrar é a direção oposta àquela para a qual você está escrevendo código: saída quebrada não é uma linha só. Se você alimentar a saída do Mime num consumidor estrito de linha única, os CRLFs viram erro de decodificação, então escolha o embrulho pelo canal, não pela conveniência. Para APIs, data URLs e qualquer coisa moderna, Default é o padrão certo, e quebrar linhas é história de e-mail e certificado.
De onde vêm os bytes?
Um codificador é tão honesto quanto os bytes que você entrega a ele, e as decisões interessantes acontecem um passo antes de encode ser chamada. A fonte mais comum é texto, e o erro mais comum é deixar o charset decidir em silêncio:
text.encodeToByteArray()é sempre UTF-8, em toda plataforma. É a escolha certa para JSON, e-mails e dados web, e a errada se o texto for Latin-1 ou UTF-16 e o outro lado decodificar de acordo.- No JVM você pode escolher explicitamente com a extensão inline
text.toByteArray(charset), que está na biblioteca padrão desde o Kotlin 1.0 e é a resposta do lado Kotlin para ogetBytes(charset)do Java. Não existegetBytesemkotlin.String, então se você escrevertext.getBytes()numa string Kotlin, o compilador vai te dizer; a extensão é o caminho.
import kotlin.io.encoding.Base64
fun main() {
val text = "héllo"
println(Base64.encode(text.encodeToByteArray())) // aMOpbGxv
println(Base64.encode(text.toByteArray(Charsets.ISO_8859_1))) // aOlsbG8=
}
As mesmas cinco letras, duas formas empacotadas diferentes, porque os bytes eram diferentes antes de qualquer Base64 entrar em cena. Se o decodificador depois assumir UTF-8, a versão Latin-1 decodifica para bagunça de caracteres, e nenhum truque de Base64 em nenhuma das pontas conserta um descompasso de charset.
Outras fontes de bytes seguem a mesma forma. Um arquivo é file.readBytes() ou path.readBytes() e depois encode. Um buffer pré-alocado usa encodeIntoByteArray(bytes, destination). Um documento em construção usa encodeToAppendable(bytes, builder), que retorna o destino para as chamadas encadear como métodos de builder:
import kotlin.io.encoding.Base64
fun main() {
val sb = StringBuilder("prefix-")
Base64.encodeToAppendable("Hello".encodeToByteArray(), sb)
println(sb) // prefix-SGVsbG8=
}
E no JVM existe uma forma em streaming para entradas que não cabem em memória, ainda marcada como experimental e importável pelo próprio nome. O truque na nomenclatura: encodingWith embrulha um stream de saída, então as escritas feitas por ele saem como base64, e os bytes base64 caem no stream de baixo:
import java.io.ByteArrayOutputStream
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.io.encoding.encodingWith
@OptIn(ExperimentalEncodingApi::class)
fun main() {
val raw = ByteArray(10_000) { (it % 251).toByte() }
val packed = ByteArrayOutputStream()
packed.encodingWith(Base64.Default).use { encoded ->
encoded.write(raw)
}
println(packed.size()) // 13336
}
A regra prática: encode em memória para tudo que cabe, encodingWith para os streams que não cabem, e um charset explícito sempre que os bytes forem de fato texto.
Notas de campo: HTTP Basic Auth
A autenticação HTTP Basic é o caso de uso de Base64 mais antigo da internet, e ainda está em todo lugar no tráfego serviço a serviço. O RFC 7617 define o esquema: pegue o usuário e a senha, junte com um único dois-pontos, codifique o resultado em base64 e embarque como Basic mais um espaço mais a string empacotada no header Authorization. No Kotlin:
import kotlin.io.encoding.Base64
fun main() {
val credentials = "alice:s3cr3t"
val header = "Basic " + Base64.encode(credentials.encodeToByteArray())
println(header) // Basic YWxpY2U6czNjcjN0
}
Por que Base64 aqui e não algo mais forte? Porque o valor de um header tem que ser um único token imprimível, e o Base64 garante isso. O aviso honesto: Base64 é codificação, não criptografia. Qualquer cliente consegue inverter YWxpY2U6czNjcjN0 para alice:s3cr3t em um passo, que é por isso que autenticação Basic pertence somente em conexões TLS, de preferência com credenciais de token em vez de senhas humanas. Quando você fizer o parse de um header assim, divida pelo dois-pontos exatamente uma vez, porque a senha pode legalmente conter dois-pontos.
Notas de campo: imagens e data URLs
Data URLs embutem assets binários diretamente em HTML, CSS e JSON para o navegador não fazer um segundo request. A forma é um tipo de mídia, uma vírgula, a palavra base64, outra vírgula, e os bytes empacotados:
import kotlin.io.encoding.Base64
fun main() {
val png = byteArrayOf(0x89.toByte(), 0x50, 0x4E, 0x47, 0x0D.toByte(), 0x0A.toByte(), 0x1A.toByte(), 0x0A.toByte())
val dataUrl = "data:image/png;base64," + Base64.encode(png)
println(dataUrl) // data:image/png;base64,iVBORw0KGgo=
}
Os bytes acima são os oito primeiros de um arquivo PNG, o número mágico que todo decodificador checa. Por que o Base64 se encaixa: o payload tem que ser um token de texto seguro para URL dentro de marcação, e o Base64 é o único binário-para-texto amplamente suportado com uma gramática estável. A armadilha é o tamanho. Um logo de 300 quilobytes vira mais ou menos 400 quilobytes de marcação, e cada quilobyte extra é pago em todo carregamento de página que o inclui. Data URLs são uma ótima ferramenta para ícones, avatares e sprites pequenos; são uma péssima ferramenta para vídeo, e uma ferramenta mediana até para uma fotografia grande. Meça antes de embutir.
Notas de campo: anexos de e-mail
SMTP é um protocolo de texto anterior a qualquer noção de binário, então todo anexo de todo e-mail que você já recebeu é Base64, quebrado em 76 caracteres, declarado com o header Content-Transfer-Encoding: base64. Uma parte MIME minimal com um pequeno anexo binário parece assim, com o corpo gerado pelo Kotlin preenchido para um header %PDF- de 5 bytes:
From: sender@example.com
To: receiver@example.com
Subject: report
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="cut-here"
--cut-here
Content-Type: text/plain; charset="utf-8"
The quarterly report follows as an attachment.
--cut-here
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="report.pdf"
JVBERi0=
--cut-here--
(O corpo JVBERi0= é o base64 dos cinco bytes %PDF-; um relatório de verdade quebraria em muitas linhas de 76 caracteres.) O lado Kotlin é uma linha quando você tem os bytes do arquivo:
import kotlin.io.encoding.Base64
fun main() {
val pdf = byteArrayOf(0x25, 0x50, 0x44, 0x46, 0x2D) // "%PDF-"
println(Base64.Mime.encode(pdf)) // JVBERi0=
}
As armadilhas são disciplina de canal. Use Mime, não Default, para o corpo, porque um parser MIME estrito espera a quebra, e uma linha de Default com 10.000 caracteres vai ser rejeitada ou amassada por alguns transportes. Mantenha o caso do header exatamente base64 na linha Content-Transfer-Encoding, e lembre que a quebra faz parte do formato: saída sem quebra e saída com quebra são representações diferentes dos mesmos bytes, e o parser do outro lado precisa saber qual está comendo.
Notas de campo: APIs JSON e uploads
Quando uma API quer binário num documento JSON, a convenção é um campo de string guardando base64, e é um dos padrões mais convenientes do ecossistema porque o JSON já tem um lugar para texto. Com kotlinx.serialization a ida e volta é direta:
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlin.io.encoding.Base64
@Serializable
data class UploadRequest(val name: String, val payload: String)
fun main() {
val icon = byteArrayOf(0x89.toByte(), 0x50, 0x4E, 0x47)
val request = UploadRequest("icon.png", Base64.encode(icon))
val json = Json.encodeToString(UploadRequest.serializer(), request)
println(json) // {"name":"icon.png","payload":"iVBORw=="}
}
Por que Base64 aqui: JSON não tem tipo binário, então o payload tem que ser texto, e o Base64 é a gramática binária de menor surpresa que um consumidor de API vai reconhecer sem documentação. A armadilha é a escala. O acréscimo de 4/3 é pago em todo request e toda resposta, e um upload de 10 megabytes vira uma string JSON de 13,3 megabytes que o seu parser precisa segurar, escapar e validar em memória. Para arquivos grandes, multipart/form-data ou um corpo binário é quase sempre o formato de linha melhor; reserve base64-em-JSON para miniaturas, ícones, assinaturas e blobs pequenos onde a conveniência supera o imposto.
Notas de campo: configuração e linha de comando
Os dois últimos padrões são os pequenos que aparecem em todo codebase. Valores de configuração, tokens, chaves de licença, às vezes segredos pequenos, muitas vezes viajam por variáveis de ambiente e arquivos de properties como base64 porque o transporte é só de texto e o valor pode conter aspas ou quebras de linha. Ler de volta é a mesma dança de dois passos ao contrário: System.getenv ou uma busca em property, e depois decodificar. Na linha de comando, codificar um arquivo para transporte ou inspeção é um programa de dez linhas:
import java.io.File
import kotlin.io.encoding.Base64
fun main(args: Array<String>) {
require(args.isNotEmpty()) { "usage: b64encode <file>" }
val bytes = File(args[0]).readBytes()
val encoded = Base64.encode(bytes)
File(args[0] + ".b64").writeText(encoded)
println("Wrote ${encoded.length} characters to ${args[0]}.b64")
}
Executado num arquivo contendo os dez bytes hello file, ele escreve 16 caracteres, aGVsbG8gZmlsZQ==. As armadilhas nos dois casos são as mesmas duas: Base64 em configuração não é um cofre, o valor está a um passo do texto claro e deve ser tratado como segredo na linha de qualquer jeito, e uma ferramenta de CLI feita à mão deveria decidir o alfabeto com deliberação, porque um usuário que mandar a sua saída para uma URL vai precisar de UrlSafe, não de Default.
O que pode dar errado na codificação
Codificar é tolerante com conteúdo: qualquer sequência de bytes é entrada válida, então não existe falha de "símbolo inválido" como os decodificadores têm. O que de fato lança exceção é geometria, e as mensagens são precisas o bastante para serem úteis:
| Situação | Exceção | Mensagem |
|---|---|---|
endIndex além do fim do array | IndexOutOfBoundsException | startIndex: 0, endIndex: 100, size: 5 |
startIndex além do endIndex | IllegalArgumentException | startIndex: 3 > endIndex: 2 |
array de destino pequeno demais para encodeIntoByteArray | IndexOutOfBoundsException | The destination array does not have enough capacity, destination offset: 0, destination size: 2, capacity needed: 8 |
Duas armadilhas específicas do Kotlin caminham ao lado dessas. A primeira é o clássico erro de int + String: bytes.size + " bytes" não compila, porque o mais num Int não concatena strings; a forma de interpolação "${bytes.size} bytes" é o jeito Kotlin. A segunda é a busca de charset, que lança UnsupportedCharsetException para um nome que o JVM não reconhece, como Charset.forName("utf-9"), então um erro de digitação num nome de charset é uma exceção em tempo de execução, não um erro de compilação, e aparece onde o codificador roda, não onde o nome foi digitado.
Armadilhas, estilo Kotlin
As armadilhas abaixo são as que mordem especificamente desenvolvedores Kotlin que pegam a biblioteca padrão pela primeira vez:
- Deixar o
encodeToByteArray()escolher o charset por você. Ele é sempre UTF-8, em silêncio, e uma fonte Latin-1 ou UTF-16 vai empacotar em bytes que o decodificador não consegue ler de volta. Decida o charset com propósito, comtoByteArray(charset)no JVM quando não for UTF-8. - Pegar
java.util.Base64por memória muscular. OgetUrlEncoder()dele aplica padding por padrão, que é a forma errada para JWTs a menos que você lembre do.withoutPadding(); o preset do Kotlin torna a escolha explícita nos dois lados. - Alimentar saída quebrada do
MimeouPemnum consumidor de linha única. Os CRLFs fazem parte da representação e vão falhar num decodificador estrito que espera uma linha só; quebre só quando o canal espera quebra. - Escrever
text.getBytes()numa string Kotlin. O método do Java não é visível emkotlin.String; a extensão inlinetoByteArray(charset), presente desde o Kotlin 1.0, é a substituta. - Rodando toolchains antigas. O Kotlin do sistema em algumas distribuições ainda é o 1.3, anterior à classe
Base64da biblioteca padrão por completo; a classe precisa do 1.8.20 para existir, do 2.0.20 para controle de padding, e do 2.2 para ser estável. - Tratar Base64 como camada de segurança. É uma codificação de transporte com um inverso público, de um passo. Qualquer coisa secreta deve ser cifrada antes de ser empacotada, nunca só empacotada.
Escolhendo bem: um guia rápido de decisão
Em dúvida, a decisão é quase sempre feita pelo canal, não pelo conteúdo. A versão curta:
Base64.Defaultpara APIs, JSON, data URLs e qualquer coisa que seja efetivamente uma linha de texto. Saída com padding é a forma mais compatível na linha.Base64.UrlSafecom paddingABSENTpara tokens, JWTs e qualquer coisa que caia num segmento de URL ou num parâmetro de query.Base64.Mimepara corpos de e-mail e anexos, onde linhas de 76 caracteres são um requisito duro do formato.Base64.Pempara certificados e chaves privadas, onde linhas de 64 caracteres é o que toda ferramenta de PKI espera.
Depois dois hábitos transversais: deixe o charset explícito sempre que a entrada for texto, e fique de olho no acréscimo de 4/3 para que payloads grandes ganhem um canal binário em vez de um base64.
O caminho até a estabilidade
O caminho da biblioteca padrão até o Base64 é recente o bastante para você encontrar Kotlin mais velho sem ele. A classe apareceu pela primeira vez no Kotlin 1.8.20, em abril de 2023, marcada como experimental, com três instâncias e uma superfície mais simples: a codificação sempre aplicava padding, e não havia como pedir menos. Se você já viu código da era 1.8 que remove o = do final de uma string com removeSuffix, essa era a única ferramenta da época para saída sem padding, e é um hábito que vale a pena abandonar agora. O Kotlin 2.2, lançado em junho de 2025, estabilizou a API e adicionou a última peça, a instância Pem. O controle PaddingOption e o withPadding já tinham chegado na linha 2.0, no 2.0.20 para ser exato, o primeiro jeito de primeira classe de controlar o padding nos dois sentidos. Os auxiliares de streaming encodingWith e decodingWith permanecem experimentais e só para JVM, que é como a biblioteca padrão marca APIs de que quer mais experiência de campo antes de congelá-las. Desde a linha 2.4.0, a linguagem também moveu para uma janela de suporte de 18 meses na biblioteca padrão, então um projeto travado num compilador 2.4.x, como o release estável 2.4.10 que é o atual até a data desta escrita, ganha a API completa de Base64 pela vida daquele ciclo de suporte.
Pequenas maravilhas
Alguns detalhes que tornam a classe mais interessante quando você os conhece:
Base64.encode(bytes)sem instância funciona porque oBase64.Defaulté definido no objeto companion; o companion é o esquema padrão, então o açúcar e a forma nomeada são literalmente o mesmo objeto.- A função
encodeToAppendabletem estilo builder: ela retorna o appendable de destino, então o padrão documentado é ignorar o valor de retorno e continuar usando o seu builder. - Padding nunca completa um grupo inteiro: uma string base64 termina com zero, um ou dois caracteres
=, e contar o padding te diz exatamente quantos bytes o original tinha sobrando na última tríade. Pemé o mais novo dos quatro presets, entrando no 2.2; a quebra de 64 caracteres é uma convenção de PKI mais antiga que a regra do RFC 2045 ao lado da qual ele mora.- No JVM a biblioteca padrão deliberadamente não delega para o
java.util.Base64; as duas implementações são separadas, o que mantém o comportamento idêntico entre plataformas ao custo de uma otimização comentada que a equipe do Kotlin manteve na árvore para um futuro em que a API do Java permita. - O mesmo release 2.2 que estabilizou o
Base64também estabilizou oHexFormat, a classe de formatação hexadecimal emkotlin.textque era experimental desde o Kotlin 1.9, então as codificações textuais em nível de byte agora têm um endereço fixo na biblioteca padrão.
Para fechar e por onde ir em seguida
Produzir Base64 no Kotlin é uma lista curta de escolhas deliberadas: escolha o preset pelo canal, decida o padding com propósito, mantenha o charset explícito quando a entrada for texto, e respeite o acréscimo de 4/3 quando o payload for grande. Todo o resto, arquivos, buffers, appendables, streams, é um embrulho fino ao redor das mesmas quatro instâncias. A outra direção, pegar esse texto empacotado de volta para bytes, tem as próprias regras de estritabilidade, os próprios modos de falha e as próprias armadilhas, e o artigo relacionado no site irmão cobre a decodificação Base64 no Kotlin em profundidade.
Última atualização: 2026-09-08
Artigo relacionado: Decodificação Base64 em Kotlin: um guia completo