Enviar um arquivo
  1. base64hub
  2. Base64 no código
  3. Kotlin
Código

Base64 em Kotlin

O Kotlin traz o objeto multiplataforma kotlin.io.encoding.Base64, estável desde o Kotlin 2.2. Na JVM, você também pode usar o java.util.Base64. As saídas abaixo vêm do Kotlin 2.4.21 no Java 21.

Codificar e decodificar uma string

O Base64.encode recebe um ByteArray e retorna uma String. O encodeToByteArray() e o decodeToString() cuidam do UTF-8 nas duas direções:

import kotlin.io.encoding.Base64

val enc = Base64.encode("héllo wörld".encodeToByteArray())
println(enc)                                // aMOpbGxvIHfDtnJsZA==
println(Base64.decode(enc).decodeToString()) // héllo wörld

O próprio Base64 é a instância padrão (Base64.Default). Também existem Base64.UrlSafe e Base64.Mime.

Em Kotlin anterior ao 2.2, a API precisa de @OptIn(ExperimentalEncodingApi::class). Se você está preso a uma versão antiga, o java.util.Base64 funciona igual ao de Base64 em Java:

java.util.Base64.getEncoder().encodeToString("hi".toByteArray())  // aGk=

Os usos típicos são headers HTTP e data URIs:

val token = Base64.encode("user:pass".encodeToByteArray())
println("Authorization: Basic $token")       // Authorization: Basic dXNlcjpwYXNz

val png = byteArrayOf(0x89.toByte(), 0x50, 0x4E, 0x47)
println("data:image/png;base64," + Base64.encode(png))  // data:image/png;base64,iVBORw==

Base64 URL-safe e opções de preenchimento

O Base64.UrlSafe troca +/ por -_. O preenchimento é controlado pelo withPadding, que retorna uma nova instância configurada:

val d = "<<???>>".encodeToByteArray()

println(Base64.UrlSafe.encode(d))                  // PDw_Pz8-Pg==

val urlNoPad = Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT)
println(urlNoPad.encode(d))                        // PDw_Pz8-Pg
println(urlNoPad.decode("PDw_Pz8-Pg").decodeToString())  // <<???>>

As quatro opções:

OpçãoCodificaçãoDecodificação
PRESENT (padrão)escreve =exige =
ABSENTsem =rejeita =
PRESENT_OPTIONALescreve =aceita as duas formas
ABSENT_OPTIONALsem =aceita as duas formas

As instâncias padrão são rígidas nisso:

Base64.UrlSafe.decode("PDw_Pz8-Pg")
// IllegalArgumentException: The padding option is set to PRESENT,
// but the input is not properly padded

Para partes de JWT e outros tokens, ABSENT_OPTIONAL ou PRESENT_OPTIONAL costuma ser a escolha certa, já que os geradores não concordam sobre o preenchimento. Contexto: Base64 vs Base64URL e preenchimento explicado.

Por exemplo, para ler o payload de um JWT:

val jwt = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.sig"
val url = Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT_OPTIONAL)
println(url.decode(jwt.split(".")[1]).decodeToString())   // {"sub":"1234"}

Quebra de linha MIME

O Base64.Mime escreve linhas de 76 caracteres separadas por \r\n e ignora os separadores de linha ao decodificar:

Base64.Mime.encode("x".repeat(60).encodeToByteArray())
// "eHh4eHh4...eHh4" (76 chars) + "\r\n" + "eHh4"

Base64.Mime.decode("aGVs\r\nbG8K").decodeToString()  // "hello\n"

Para comparar, cole a mesma entrada no codificador Base64 MIME.

Codificar e decodificar um arquivo

Arquivos pequenos cabem na memória:

import java.io.File

println(Base64.encode(File("hello.txt").readBytes()))   // aGVsbG8K
File("copy.txt").writeBytes(Base64.decode("aGVsbG8K"))

Streaming de arquivos grandes

Na JVM, o encodingWith e o decodingWith envolvem streams. Eles ainda são experimentais no Kotlin 2.4, então exigem opt-in e imports próprios:

import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.io.encoding.decodingWith
import kotlin.io.encoding.encodingWith
import java.io.File

@OptIn(ExperimentalEncodingApi::class)
fun encodeFile(src: File, dst: File) {
    dst.outputStream().encodingWith(Base64).use { out ->
        src.inputStream().use { it.copyTo(out) }
    }
}

@OptIn(ExperimentalEncodingApi::class)
fun decodeFile(src: File, dst: File) {
    src.inputStream().decodingWith(Base64).use { inp ->
        dst.outputStream().use { inp.copyTo(it) }
    }
}

Ao fechar o stream de codificação, o que o bloco use faz por você, o último grupo e o preenchimento são escritos. A saída em streaming de um arquivo de teste de 300 KB foi idêntica à de Base64.encode(readBytes()), e a ida e volta devolveu os mesmos bytes. Se preferir um caminho não experimental, o java.util.Base64.getEncoder().wrap(outputStream) faz o mesmo trabalho.

Validação e mensagens de exceção

O decodificador do Kotlin é mais rígido que o do Java. Ele rejeita bits de preenchimento diferentes de zero, que o Java e a maioria das outras bibliotecas aceitam. Toda falha é uma IllegalArgumentException:

Entrada para Base64.decodeMensagem
PDw_Pz8-Pg==Invalid symbol '_'(137) at index 3
"aGVs\nbG8K"Invalid symbol '\n'(12) at index 4
aGk=!!Symbol '!'(41) at index 3 is prohibited after the pad character
aGk==Symbol '='(75) at index 3 is prohibited after the pad character
aGl=The pad bits must be zeros
aInput should have at least 2 symbols for Base64 decoding, startIndex: 0, endIndex: 1

O número entre parênteses é o código do caractere em octal (137 é _, 12 é uma quebra de linha). Um helper de validação cabe numa linha:

fun isBase64(s: String) = runCatching { Base64.decode(s) }.isSuccess

Para ver onde uma string específica quebra, use o validador de Base64; o guia de erros de Base64 lista as causas habituais.

Armadilhas

  • Entrada sem preenchimento com a instância padrão. Segmentos de JWT e muitos tokens não têm =. Configure PRESENT_OPTIONAL ou ABSENT_OPTIONAL em vez de completar strings à mão.
  • Flag DEFAULT do Android. android.util.Base64.encodeToString(bytes, Base64.DEFAULT) insere uma quebra de linha a cada 76 caracteres e no final. Num header HTTP, isso quebra a requisição. Use NO_WRAP, ou NO_WRAP or URL_SAFE or NO_PADDING para tokens.
  • Charset do String.toByteArray(). No Kotlin o padrão é UTF-8, ao contrário do getBytes() do Java em JDKs antigos, então "héllo".toByteArray() é seguro.
  • Decodificar binário para string. decodeToString() em bytes de imagem substitui sequências inválidas por U+FFFD e corrompe os dados. Mantenha o ByteArray.

Notas de versão

  • Kotlin 1.8.20: kotlin.io.encoding.Base64 adicionado como experimental.
  • Kotlin 2.0.20: withPadding e PaddingOption.
  • Kotlin 2.2: a API principal se tornou estável; os helpers de stream da JVM continuam experimentais.

Perguntas frequentes

Ainda preciso de @OptIn(ExperimentalEncodingApi::class)?

Não para Base64.encode e Base64.decode no Kotlin 2.2 ou mais novo. Os helpers de stream encodingWith e decodingWith ainda exigem o opt-in no Kotlin 2.4.

Como decodificar Base64 sem preenchimento em Kotlin?

Use uma instância configurada: Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT) para entrada sem preenchimento, ou PRESENT_OPTIONAL para aceitar as duas formas. As instâncias padrão exigem preenchimento.

Devo usar android.util.Base64 ou kotlin.io.encoding.Base64?

Em código novo, a API do Kotlin funciona em qualquer versão do Android porque faz parte da biblioteca padrão. O android.util.Base64 acrescenta quebras de linha a menos que você passe Base64.NO_WRAP, uma fonte frequente de headers quebrados.

Continue lendo