Datei hochladen
  1. base64hub
  2. Base64 im Code
  3. Kotlin
Code

Base64 in Kotlin

Kotlin liefert ein Multiplattform-Objekt kotlin.io.encoding.Base64 mit, stabil seit Kotlin 2.2. Auf der JVM können Sie außerdem java.util.Base64 verwenden. Die Ausgaben unten stammen von Kotlin 2.4.21 auf Java 21.

Einen String kodieren und dekodieren

Base64.encode nimmt ein ByteArray entgegen und liefert einen String. encodeToByteArray() und decodeToString() kümmern sich in beide Richtungen um UTF-8:

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

Base64 selbst ist die Standardinstanz (Base64.Default). Daneben gibt es Base64.UrlSafe und Base64.Mime.

Unter Kotlin vor 2.2 braucht die API @OptIn(ExperimentalEncodingApi::class). Hängen Sie an einer alten Version fest, funktioniert java.util.Base64 genauso wie unter Base64 in Java:

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

Typische Einsatzzwecke sind HTTP-Header und 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==

URL-sicheres Base64 und Padding-Optionen

Base64.UrlSafe tauscht +/ gegen -_. Das Padding steuert withPadding, das eine neue konfigurierte Instanz liefert:

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())  // <<???>>

Die vier Optionen:

OptionKodierenDekodieren
PRESENT (Standard)schreibt =verlangt =
ABSENTkein =lehnt = ab
PRESENT_OPTIONALschreibt =akzeptiert beides
ABSENT_OPTIONALkein =akzeptiert beides

Die Standardinstanzen sind dabei streng:

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

Für JWT-Teile und andere Tokens ist ABSENT_OPTIONAL oder PRESENT_OPTIONAL meist die richtige Wahl, da sich die Erzeuger beim Padding nicht einig sind. Hintergrund: Base64 vs. Base64URL und Padding erklärt.

So lesen Sie zum Beispiel die Payload eines JWT:

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

MIME-Zeilenumbruch

Base64.Mime schreibt Zeilen mit 76 Zeichen, getrennt durch \r\n, und ignoriert Zeilentrenner beim Dekodieren:

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

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

Zum Vergleich fügen Sie dieselbe Eingabe in den MIME-Base64-Encoder ein.

Eine Datei kodieren und dekodieren

Kleine Dateien passen in den Speicher:

import java.io.File

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

Große Dateien streamen

Auf der JVM umhüllen encodingWith und decodingWith Streams. In Kotlin 2.4 sind sie noch experimentell und brauchen daher ein Opt-in und eigene Imports:

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) }
    }
}

Erst das Schließen des kodierenden Streams, das der use-Block für Sie übernimmt, schreibt die letzte Gruppe und das Padding. Die gestreamte Ausgabe einer 300-KB-Testdatei war identisch mit Base64.encode(readBytes()), und der Roundtrip lieferte dieselben Bytes zurück. Bevorzugen Sie einen nicht experimentellen Weg, erledigt java.util.Base64.getEncoder().wrap(outputStream) dieselbe Aufgabe.

Validierung und Exception-Meldungen

Der Kotlin-Decoder ist strenger als der von Java. Er lehnt Padding-Bits ungleich null ab, die Java und die meisten anderen Bibliotheken akzeptieren. Jeder Fehler ist eine IllegalArgumentException:

Eingabe für Base64.decodeMeldung
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

Die Zahl in Klammern ist der Zeichencode in Oktal (137 ist _, 12 ist ein Zeilenumbruch). Eine Prüffunktion passt in eine Zeile:

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

Wo genau ein bestimmter String bricht, zeigt der Base64-Validator; die Anleitung zu Base64-Fehlern listet die üblichen Ursachen auf.

Fallstricke

  • Eingaben ohne Padding mit der Standardinstanz. JWT-Segmente und viele Tokens haben kein =. Konfigurieren Sie PRESENT_OPTIONAL oder ABSENT_OPTIONAL, statt Strings von Hand aufzufüllen.
  • Android-Flag DEFAULT. android.util.Base64.encodeToString(bytes, Base64.DEFAULT) fügt alle 76 Zeichen und am Ende einen Zeilenumbruch ein. In einem HTTP-Header macht das die Anfrage kaputt. Verwenden Sie NO_WRAP, für Tokens NO_WRAP or URL_SAFE or NO_PADDING.
  • Zeichensatz von String.toByteArray(). In Kotlin ist der Standard UTF-8, anders als bei Javas getBytes() auf älteren JDKs; "héllo".toByteArray() ist also sicher.
  • Binärdaten in einen String dekodieren. decodeToString() auf Bild-Bytes ersetzt ungültige Sequenzen durch U+FFFD und beschädigt die Daten. Behalten Sie das ByteArray.

Versionshinweise

  • Kotlin 1.8.20: kotlin.io.encoding.Base64 als experimentell hinzugefügt.
  • Kotlin 2.0.20: withPadding und PaddingOption.
  • Kotlin 2.2: Die Kern-API wurde stabil; die JVM-Stream-Hilfsfunktionen bleiben experimentell.

Häufige Fragen

Brauche ich noch @OptIn(ExperimentalEncodingApi::class)?

Für Base64.encode und Base64.decode ab Kotlin 2.2 nicht mehr. Die Stream-Hilfsfunktionen encodingWith und decodingWith verlangen das Opt-in in Kotlin 2.4 weiterhin.

Wie dekodiere ich in Kotlin Base64 ohne Padding?

Verwenden Sie eine konfigurierte Instanz: Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT) für Eingaben ohne Padding oder PRESENT_OPTIONAL, um beides zu akzeptieren. Die Standardinstanzen verlangen Padding.

Soll ich android.util.Base64 oder kotlin.io.encoding.Base64 verwenden?

In neuem Code funktioniert die Kotlin-API auf jeder Android-Version, weil sie zur Standardbibliothek gehört. android.util.Base64 fügt Zeilenumbrüche ein, sofern Sie nicht Base64.NO_WRAP übergeben. Das ist eine häufige Ursache für kaputte Header.

Weiterlesen