Haben Sie mit dem Base64-Format zu tun? Dann ist diese Website genau das Richtige für Sie! Nutzen Sie unser superpraktisches Online-Tool, um Ihre Daten zu kodieren oder zu dekodieren.

Base64-Kodierung in Kotlin: Ein vollständiger Leitfaden

Die Hälfte jedes Base64-Gesprächs dreht sich darum, gepackte Daten wieder in Bytes zurückzulesen. Die andere Hälfte, der Teil, für den Sie auf der richtigen Site sind, dreht sich darum, diese gepackten Daten überhaupt erst zu produzieren. Irgendwo in Ihrer Anwendung gibt es Bytes, die durch einen Kanal reisen müssen, der nur Text versteht: einen JSON-String, einen HTTP-Header, eine E-Mail, eine URL, eine Konfigurationsdatei. Base64 ist die klassische Antwort, und Kotlin hat in der Standardbibliothek eine erstklassige Antwort darauf: die Base64-Klasse in kotlin.io.encoding, stabil seit Kotlin 2.2.

Dieser Leitfaden geht durch, was Sie wirklich brauchen, wenn Sie derjenige sind, der Base64 produziert: die vier Preset-Schemata, den Padding-Regler, die Zeilenumbruch-Regeln, nach denen E-Mail und Zertifikate leben, das URL-sichere Alphabet, woher die Bytes kommen, und eine Reihe realer Szenarien, in jedem davon mit Kotlin. Das Format selbst, wie 3 Bytes zu 4 Zeichen werden, woher das = kommt, wird auf der Startseite behandelt, also kommen wir hier direkt zum Kotlin.

Eine Klasse, vier Presets

Die gesamte API ist eine einzige Klasse, Base64, im kotlin.io.encoding-Paket. Es gibt kein encoder-Objekt, das Sie konstruieren, und keinen Builder. Stattdessen kommt die Klasse mit vier Preset-Instanzen, je eine pro RFC-Schema, und einem Companion-Objekt, das still und heimlich für die gebräuchlichste einspringt:

InstanzAlphabetZeilenumbruch beim KodierenPadding beim KodierenWofür Sie sie nutzen
Base64.Default+ und /keinererzeugt =Allzweck, APIs, Data-URLs
Base64.UrlSafe- und _keinererzeugt = (ausschaltbar)URLs, Tokens, JWTs
Base64.Mime+ und /CRLF alle 76 Zeichenerzeugt =E-Mail-Texte und Anhänge
Base64.Pem+ und /CRLF alle 64 Zeichenerzeugt =Zertifikate und private Schlüssel

Das Benennungs-Detail, über das Leute stolpern: Das sind Instanzen, keine Fabriken. Jede Instanz ist ein unveränderlicher Wert, und eine Änderung ihres Verhaltens, wie des Paddings, gibt eine neue Instanz zurück, statt die alte zu verändern. Das macht die Presets sicher zum Teilen über Threads hinweg und zum Ablegen in Objekten, und genau deshalb kann die ganze Klasse ein einfacher Wertetyp ohne internen Zustand sein.

Ihre erste Kodierung: Bytes hinein, String heraus

Hier ist das kleinste nützliche Programm auf der Site: fünf Bytes hinein, ein acht Zeichen langer String heraus. Die Eingabe ist immer ein ByteArray (oder ein Ausschnitt daraus), und das Ergebnis ist ein einfacher String, den Sie überall hinsetzen können, wo Text erlaubt ist:

import kotlin.io.encoding.Base64
fun main() {
  val bytes = "Hello".encodeToByteArray()
  val packed = Base64.encode(bytes)
  println(packed)  // SGVsbG8=
}

Diese eine Zeile leistet mehr, als sie aussieht. Kotlin gibt Ihnen mehrere Formen derselben Operation, und sie alle lesen sich so, wie die Funktion es sagt:

  • encode(bytes) gibt einen String zurück, die Form oben.
  • encodeToByteArray(bytes) gibt ein ByteArray aus ASCII-Zeichen zurück, praktisch, wenn die gepackte Form selbst in einen anderen Buffer kommt.
  • encodeIntoByteArray(bytes, destination) schreibt in ein ByteArray, das Sie schon alloziert haben, was eine Allokation auf heißen Pfaden spart.
  • encodeToAppendable(bytes, builder) hängt an alles an, was Appendable implementiert, wie einen StringBuilder, was der natürliche Fall ist, wenn Sie ein größeres Dokument zusammenbauen.

Alle vier akzeptieren dasselbe optionale startIndex- und endIndex-Intervall, also können Sie einen Ausschnitt eines großen Buffers packen, ohne ihn zuerst zu kopieren. Weil Base64.Default das Companion-Objekt ist, können Sie auch die Instanz weglassen und Base64.encode(bytes) als Zucker schreiben; beide Formen sind derselbe Aufruf.

Die 4/3-Regel: Wie lang wird es?

Bevor Sie einen Kodierer ausliefern, lohnt es sich zu wissen, genau wie viel größer die Ausgabe wird, weil Base64 Zeichen für Informationen ausgibt, die es schon hatte. Die Mathematik ist strikt: Jede Gruppe von drei Eingabe-Bytes wird zu exakt vier Ausgabe-Zeichen, also verbrauchen auch 1 oder 2 übrig gebliebene Bytes eine volle Gruppe von 4, aufgefüllt mit =, um die Gruppe voll zu machen. Das Ergebnis für die ersten Größen:

Eingabe-Bytes12345678
Ausgabe-Zeichen4448881212

Die Formel hinter der Tabelle ist 4 * ceil(bytes / 3). Im schlimmsten Fall wird ein einzelnes Byte zu 4 Zeichen, ein 300-Prozent-Aufschlag; ab drei Bytes konvergiert es gegen etwa ein Drittel mehr Daten auf der Leitung. Das ist das gesamte Kostenmodell; es gibt keine Variation pro Instanz, und deshalb sollten Sie beim Kodieren großer Payloads mit Base64 gezielt vorgehen, statt es aus Reflex zu greifen.

Padding ist eine Einstellung, kein Schicksal

Auf der Kodierungsseite sind die =-Zeichen eine Politik-Entscheidung, und Kotlin macht sie zu einer ersten-Klasse-Entscheidung. Jede Instanz trägt eine PaddingOption, und withPadding gibt Ihnen eine neue Instanz mit verschobenem Regler. Alle vier Presets starten auf PRESENT, deshalb kommt "Hello" als SGVsbG8= und nicht als SGVsbG8 heraus:

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
}

Der Regler hat vier Positionen. Das erste Wort des Namens entscheidet, was der Kodierer ausgibt; die zweite Hälfte entscheidet, wie strikt der Dekoder derselben Instanz sein wird, wenn Sie (oder die andere Seite) ihn später umdrehen:

PaddingOptionKodierer erzeugt =Dekoder akzeptiert =
PRESENTjaerforderlich, alles andere scheitert
ABSENTneinverboten, ein verirrtes Padding scheitert
PRESENT_OPTIONALjabeides
ABSENT_OPTIONALneinbeides

Die häufigste Wahl zur Kodierungszeit ist ABSENT mit dem UrlSafe-Alphabet, was exakt die Form ist, die JSON Web Tokens und viele URL-Schemata erwarten. Auf sie stoßen Sie gleich wieder.

Base64url: URLs, Tokens und JWTs

Das klassische Alphabet enthält + und /, und beide sind in URLs Katastrophen: Ein + in einem Query-String wird routinemäßig als Leerzeichen gelesen, und ein / ist der Pfadtrenner. RFC 4648 Abschnitt 5 definiert die URL-sichere Variante, die - und _ einschiebt, und Base64.UrlSafe ist dieses Schema. Dieselben Bytes zu kodieren, die im klassischen Alphabet ein / produzieren, zeigt den Tausch in Aktion:

import kotlin.io.encoding.Base64
fun main() {
  val bytes = "Hello?".encodeToByteArray()
  println(Base64.encode(bytes))          // SGVsbG8/
  println(Base64.UrlSafe.encode(bytes))  // SGVsbG8_
}

Der kanonische Anwender aus der Praxis ist ein JWT, dessen Header und Payload base64url ohne Padding sind, verbunden durch Punkte. Hier ist die Kodierungshälfte des Aufbaus, eine Form, die Sie verstehen sollten, selbst wenn eine Bibliothek das finale Token signiert:

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

Gibt aus:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.Ym9nVXNlZlNpZ25hdHVyZUZvckRlbW8

Zwei Warnungen gehören hierher. Erstens: Das dritte Segment ist eine Signatur, und um eine echte zu produzieren, brauchen Sie echte Kryptographie (einen JCA/JCE-Signer oder eine JWT-Bibliothek), niemals von Hand gerollte Bytes; das Snippet oben demonstriert nur die Kodierungsform. Zweitens: Wenn Sie auf der JVM sind und aus Gewohnheit nach java.util.Base64.getUrlEncoder() greifen, beachten Sie, dass dieser standardmäßig paddet, also braucht JWT-ähnliche Ausgabe dort .withoutPadding(); das Kotlin-Preset paddet von Haus aus ebenso lautstark, und Sie steigen mit einem withPadding-Aufruf aus.

Zeilenumbruch: Die Mime- und Pem-Presets

Zwei der vier Presets brechen ihre Ausgabe in kurze Zeilen um, und der Grund ist historisch. Alte E-Mail-Transporte haben lange Zeilen kaputtgemacht, also deckelt RFC 2045 Abschnitt 6.8 MIME-Base64 auf 76 Zeichen pro Zeile; PKI-Tools verwenden, der älteren PEM-Tradition folgend, 64. Kotlin baut beide Regeln direkt in das Preset ein: Der Zeilentrenner ist CRLF, der Bruch landet exakt an der Grenze, und am ganz Ende gibt es keinen abschließenden Trenner. Ein 200-Byte-Payload durch jeden der beiden Wrapper sieht so aus:

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
}

Für diese Eingabe produziert Mime 4 Zeilen und Pem 5. Die erste Mime-Zeile ist:

AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4

Die Falle, die man sich merken sollte, ist die entgegengesetzte Richtung derjenigen, für die Sie gerade Code schreiben: umgebrochene Ausgabe ist keine einzelne Zeile. Wenn Sie Mime-Ausgabe einem strikten Einzelzeilen-Verbraucher füttern, werden die CRLFs zu einem Dekodierungsfehler, also wählen Sie den Umbruch nach Kanal, nicht nach Bequemlichkeit. Für APIs, Data-URLs und alles Moderne ist Default der richtige Standard, und Umbruch ist eine E-Mail-und-Zertifikate-Sache.

Woher kommen die Bytes?

Ein Kodierer ist nur so ehrlich wie die Bytes, die Sie ihm übergeben, und die interessanten Entscheidungen fallen einen Schritt vor dem Aufruf von encode. Die häufigste Quelle ist Text, und der häufigste Fehler ist, den Zeichensatz stillschweigend entscheiden zu lassen:

  • text.encodeToByteArray() ist immer UTF-8, auf jeder Plattform. Es ist die richtige Wahl für JSON, E-Mails und Webdaten und die falsche, wenn der Text Latin-1 oder UTF-16 ist und die andere Seite entsprechend dekodiert.
  • Auf der JVM können Sie explizit mit der Inline-Extension text.toByteArray(charset) wählen, die seit Kotlin 1.0 in der Standardbibliothek ist - die Kotlin-seitige Antwort auf Javas getBytes(charset). Es gibt kein getBytes auf kotlin.String, also wird Ihnen der Compiler Bescheid sagen, wenn Sie text.getBytes() auf einem Kotlin-String schreiben; die Extension ist der Weg.
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=
}

Dieselben fünf Buchstaben, zwei verschiedene gepackte Formen, weil die Bytes anders waren, bevor überhaupt Base64 ins Spiel kam. Wenn der Dekoder später UTF-8 annimmt, dekodiert die Latin-1-Version zu Mojibake, und kein Base64-Trick auf irgendeiner Seite kann einen Zeichensatz-Mismatch reparieren.

Andere Quellen von Bytes folgen derselben Form. Eine Datei ist file.readBytes() oder path.readBytes() und dann encode. Ein vorallozierter Buffer nutzt encodeIntoByteArray(bytes, destination). Ein Dokument im Bau nutzt encodeToAppendable(bytes, builder), das das Ziel zurückgibt, sodass Aufrufe wie Builder-Methoden kettbar sind:

import kotlin.io.encoding.Base64
fun main() {
  val sb = StringBuilder("prefix-")
  Base64.encodeToAppendable("Hello".encodeToByteArray(), sb)
  println(sb)  // prefix-SGVsbG8=
}

Und auf der JVM gibt es eine Streaming-Form für Eingaben, die nicht in den Speicher passen, immer noch als experimentell markiert und unter ihrem eigenen Namen importierbar. Der Twist in der Benennung: encodingWith verpackt einen Ausgabe-Stream, also kommen Schreibvorgänge, die darüber gemacht werden, als base64 heraus, und die base64-Bytes landen im zugrunde liegenden Stream:

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
}

Die Faustregel: In-memory-encode für alles, was passt, encodingWith für die Streams, die nicht passen, und ein expliziter Zeichensatz, wann immer die Bytes tatsächlich Text sind.

Feldnotizen: HTTP Basic Auth

Die HTTP Basic-Authentifizierung ist der älteste Base64-Fall im Internet, und sie ist nach wie vor überall im Service-zu-Service-Verkehr anzutreffen. RFC 7617 definiert das Schema: Nehmen Sie Benutzer und Passwort, verbinden Sie sie mit einem einzelnen Doppelpunkt, kodieren Sie das Ergebnis mit Base64 und versenden Sie es im Authorization-Header als Basic plus Leerzeichen plus den gepackten String. In Kotlin:

import kotlin.io.encoding.Base64
fun main() {
  val credentials = "alice:s3cr3t"
  val header = "Basic " + Base64.encode(credentials.encodeToByteArray())
  println(header)  // Basic YWxpY2U6czNjcjN0
}

Warum hier Base64 und nicht etwas Stärkeres? Weil ein Header-Wert ein einzelner druckbarer Token sein muss, und Base64 das garantiert. Die ehrliche Warnung: Base64 ist Kodierung, keine Verschlüsselung. Jeder Client kann YWxpY2U6czNjcjN0 in einem Schritt zurück zu alice:s3cr3t drehen, weshalb Basic Auth nur auf TLS-Verbindungen gehört, idealerweise mit Token-Credentials statt menschlicher Passwörter. Wenn Sie einen solchen Header parsen, trennen Sie beim Doppelpunkt genau einmal, denn das Passwort darf legal Doppelpunkte enthalten.

Feldnotizen: Bilder und Data-URLs

Data-URLs betten binäre Assets direkt in HTML, CSS und JSON ein, damit der Browser keine zweite Anfrage stellt. Die Form ist ein Medientyp, ein Komma, das Wort base64, ein weiteres Komma und die gepackten Bytes:

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

Die Bytes oben sind die ersten acht einer PNG-Datei, die magische Zahl, die jeder Dekoder prüft. Warum Base64 passt: Das Payload muss ein URL-sicheres Text-Token innerhalb von Markup sein, und Base64 ist das einzige weit unterstützte Binär-zu-Text-Format mit stabiler Grammatik. Die Falle ist die Größe. Ein 300-Kilobyte-Logo wird zu etwa 400 Kilobyten Markup, und jedes zusätzliche Kilobyte wird bei jedem Seiten-Load bezahlt, der es enthält. Data-URLs sind ein großartiges Werkzeug für Icons, Avatare und kleine Sprites; sie sind ein furchtbares Werkzeug für Video und selbst ein mittelmäßiges Werkzeug für ein großes Foto. Messen Sie, bevor Sie inline machen.

Feldnotizen: E-Mail-Anhänge

SMTP ist ein Textprotokoll, das älter ist als jede Vorstellung von Binärdaten, also ist jeder Anhang in jeder E-Mail, die Sie je erhalten haben, Base64, umgebrochen bei 76 Zeichen, deklariert mit einem Content-Transfer-Encoding: base64-Header. Ein minimales MIME-Teil mit einem kleinen binären Anhang sieht so aus, mit dem Kotlin-erzeugten Body-Slot, ausgefüllt für einen 5-Byte-%PDF--Header:

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

(Der Body JVBERi0= ist das base64 der fünf Bytes %PDF-; ein echter Bericht würde sich über viele 76-Zeichen-Zeilen umwickeln.) Die Kotlin-Seite ist eine Zeile, wenn Sie die Datei-Bytes haben:

import kotlin.io.encoding.Base64
fun main() {
  val pdf = byteArrayOf(0x25, 0x50, 0x44, 0x46, 0x2D)  // "%PDF-"
  println(Base64.Mime.encode(pdf))  // JVBERi0=
}

Die Fallen sind Kanal-Disziplin. Nutzen Sie für den Body Mime, nicht Default, denn ein strikter MIME-Parser erwartet den Umbruch, und eine einfache Default-Zeile von 10.000 Zeichen wird von manchen Transporten abgelehnt oder kaputtgemacht. Halten Sie die Header-Großschreibung exakt bei base64 in der Content-Transfer-Encoding-Zeile, und denken Sie daran, dass der Umbruch Teil des Formats ist: nicht umgebrochene und umgebrochene Ausgabe sind verschiedene Darstellungen derselben Bytes, und der Parser auf der anderen Seite muss wissen, welche er isst.

Feldnotizen: JSON-APIs und Uploads

Wenn eine API Binärdaten in ein JSON-Dokument will, ist die Konvention ein String-Feld, das base64 hält, und sie ist eines der bequemsten Muster im Ökosystem, weil JSON bereits ein Zuhause für Text hat. Mit kotlinx.serialization ist der Roundtrip geradlinig:

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=="}
}

Warum Base64 hier: JSON hat keinen binären Typ, also muss das Payload Text sein, und Base64 ist die überraschungsärmste binäre Grammatik, die ein API-Verbraucher ohne Dokumentation erkennt. Die Falle ist die Skala. Der 4/3-Aufschlag wird bei jeder Anfrage und jeder Antwort bezahlt, und ein 10-Megabyte-Upload wird zu einem 13,3-Megabyte-JSON-String, den Ihr Parser im Speicher halten, escapen und validieren muss. Für große Dateien ist multipart/form-data oder ein binärer Body fast immer das bessere Wire-Format; halten Sie base64-in-JSON für Thumbnails, Icons, Signaturen und kleine Blobs vor, wo die Bequemlichkeit die Steuer aufwiegt.

Feldnotizen: Konfiguration und Kommandozeile

Die letzten zwei Muster sind die kleinen, die in jeder Codebase vorkommen. Konfigurationswerte, Tokens, Lizenzschlüssel, manchmal kleine Geheimnisse reisen oft als base64 durch Umgebungsvariablen und Properties-Dateien, weil der Transport nur Text ist und der Wert Anführungszeichen oder Zeilenumbrüche enthalten kann. Sie zurückzulesen ist derselbe Zwei-Schritte-Tanz in umgekehrter Reihenfolge: System.getenv oder eine Property-Suche, dann dekodieren. Auf der Kommandozeile ist das Kodieren einer Datei zum Transport oder zur Inspektion ein Programm von zehn Zeilen:

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

Auf einer Datei mit den zehn Bytes hello file ausgeführt, schreibt es 16 Zeichen, aGVsbG8gZmlsZQ==. Die Fallen in beiden Fällen sind dieselben zwei: Base64 in der Konfiguration ist kein Tresor, der Wert ist einen Schritt von Klartext entfernt und sollte auf dem Draht ohnehin als Geheimnis behandelt werden, und ein von Hand gerolltes CLI-Werkzeug sollte sein Alphabet bewusst entscheiden, denn ein Nutzer, der Ihre Ausgabe in eine URL pumpt, braucht UrlSafe, nicht Default.

Was beim Kodieren schiefgehen kann

Kodierung ist nachsichtig gegenüber dem Inhalt: Jede Byte-Folge ist gültige Eingabe, also gibt es keinen "Ungültiges Symbol"-Fehler, wie er bei Dekodern vorkommt. Was wohl wirft, ist Geometrie, und die Meldungen sind präzise genug, um nützlich zu sein:

SituationAusnahmeMeldung
endIndex über das Ende des Arrays hinausIndexOutOfBoundsExceptionstartIndex: 0, endIndex: 100, size: 5
startIndex über endIndex hinausIllegalArgumentExceptionstartIndex: 3 > endIndex: 2
Ziel-Array zu klein für encodeIntoByteArrayIndexOutOfBoundsExceptionThe destination array does not have enough capacity, destination offset: 0, destination size: 2, capacity needed: 8

Zwei Kotlin-spezifische Fallen sitzen neben diesen. Die erste ist der klassische int + String-Fehler: bytes.size + " bytes" kompiliert nicht, denn plus auf einem Int konkateniert keine Strings; die Interpolations-Form "${bytes.size} bytes" ist der Kotlin-Weg. Die zweite ist die Zeichensatz-Suche, die für einen Namen, den die JVM nicht erkennt, wie Charset.forName("utf-9"), UnsupportedCharsetException wirft, also ist ein Tippfehler in einem Zeichensatz-Namen eine Laufzeit-Ausnahme, kein Kompilierfehler, und er taucht dort auf, wo der Kodierer läuft, nicht dort, wo der Name getippt wurde.

Fallen, Kotlin-Style

Die Fallen unten sind die, die Kotlin-Entwickler speziell beißen, die zum ersten Mal nach der Standardbibliothek greifen:

  • Den Zeichensatz von encodeToByteArray() für Sie wählen zu lassen. Er ist immer UTF-8, stillschweigend, und eine Latin-1- oder UTF-16-Quelle wird in Bytes gepackt, die der Dekoder nicht zurücklesen kann. Entscheiden Sie den Zeichensatz mit Absicht, mit toByteArray(charset) auf der JVM, wenn es kein UTF-8 ist.
  • Nach java.util.Base64 aus Muskelgedächtnis zu greifen. Sein getUrlEncoder() paddet standardmäßig, was die falsche Form für JWTs ist, es sei denn, Sie denken an .withoutPadding(); das Kotlin-Preset macht die Wahl auf beiden Seiten explizit.
  • Mime- oder Pem-umgebrochene Ausgabe an einen Einzelzeilen-Verbraucher zu füttern. Die CRLFs sind Teil der Darstellung und werden einen strikten Dekoder, der eine Zeile erwartet, zum Scheitern bringen; brechen Sie nur um, wenn der Kanal Umbruch erwartet.
  • text.getBytes() auf einem Kotlin-String zu schreiben. Die Java-Methode ist auf kotlin.String nicht sichtbar; die Inline-Extension toByteArray(charset), vorhanden seit Kotlin 1.0, ist der Ersatz.
  • Alte Toolchains zu fahren. Das System-Kotlin auf manchen Distributionen ist immer noch 1.3, was komplett vor der Standardbibliotheks-Base64 liegt; die Klasse braucht 1.8.20, um zu existieren, 2.0.20 für die Padding-Kontrolle und 2.2, um stabil zu sein.
  • Base64 als Sicherheitsschicht zu behandeln. Es ist eine Transport-Kodierung mit einem öffentlichen, einstufigen Inversen. Alles Geheimnisvolle sollte verschlüsselt werden, bevor es gepackt wird, niemals nur gepackt.

Gut wählen: Eine kurze Entscheidungsanleitung

Im Zweifel wird die Entscheidung fast immer vom Kanal getroffen, nicht vom Inhalt. Die kurze Version:

  • Base64.Default für APIs, JSON, Data-URLs und alles, was im Grunde eine Textzeile ist. Gepaddete Ausgabe ist die kompatibelste Form auf der Leitung.
  • Base64.UrlSafe mit ABSENT-Padding für Tokens, JWTs und alles, was in ein URL-Segment oder einen Query-Parameter landet.
  • Base64.Mime für E-Mail-Texte und Anhänge, wo 76-Zeichen-Zeilen eine harte Anforderung des Formats sind.
  • Base64.Pem für Zertifikate und private Schlüssel, wo 64-Zeichen-Zeilen das sind, was jedes PKI-Tool erwartet.

Dann zwei übergreifende Gewohnheiten: den Zeichensatz explizit machen, wann immer die Eingabe Text ist, und ein Auge auf den 4/3-Aufschlag haben, damit große Payloads einen binären Kanal statt eines base64-igen bekommen.

Der Weg zum Standard

Der Weg der Standardbibliothek zu Base64 ist jung genug, dass Sie älteres Kotlin ohne sie antreffen werden. Die Klasse tauchte erstmals in Kotlin 1.8.20 im April 2023 auf, als experimentell markiert, mit drei Instanzen und einer einfacheren Oberfläche: Kodierung hatte immer Padding, und es gab keinen Weg, nach weniger zu fragen. Wenn Sie Code aus der 1.8-Ära gesehen haben, der = mit removeSuffix vom Ende eines Strings streift, dann war das das einzige Werkzeug der Ära für ungepaddete Ausgabe, und es ist eine Gewohnheit, die sich jetzt abzulegen lohnt. Kotlin 2.2, veröffentlicht im Juni 2025, stabilisierte die API und fügte das letzte Teil hinzu, die Pem-Instanz. Der PaddingOption-Regler und withPadding waren schon in der 2.0-Linie angekommen, in 2.0.20 um genau zu sein, der erste erstklassige Weg, um Padding in beide Richtungen zu steuern. Die Streaming-Helfer encodingWith und decodingWith bleiben experimentell und nur für die JVM, so markiert die Standardbibliothek APIs, bei denen sie vor dem Einfrieren mehr Praxiserfahrung will. Seit der 2.4.0-Linie ist die Sprache auch zu einem 18-Monate-Unterstützungsfenster für die Standardbibliothek gewechselt, also bekommt ein Projekt, das an einen 2.4.x-Compiler gepinnt ist, wie das 2.4.10-Release, das zum Zeitpunkt dieses Schreibens aktuell ist, die vollständige Base64-API für die Dauer dieses Unterstützungszyklus.

Kleine Wunder

Ein paar Details, die die Klasse interessanter machen, wenn man sie kennt:

  • Base64.encode(bytes) ohne Instanz funktioniert, weil Base64.Default auf dem Companion-Objekt definiert ist; das Companion ist das Standard-Schema, also sind die Zucker-Form und die benannte Form buchstäblich dasselbe Objekt.
  • Die encodeToAppendable-Funktion ist Builder-Style: Sie gibt das Ziel-Appendable zurück, also ist das dokumentierte Muster, den Rückgabewert zu ignorieren und Ihren Builder weiterzunutzen.
  • Padding füllt nie eine ganze Gruppe: Ein base64-String endet mit null, einem oder zwei =-Zeichen, und das Zählen des Paddings sagt Ihnen exakt, wie viele Bytes das Original in der letzten Drei-Byte-Gruppe übrig hatte.
  • Pem ist die jüngste der vier Presets, hinzugekommen in 2.2; der 64-Zeichen-Umbruch ist eine PKI-Konvention, die älter ist als die RFC-2045-Regel, neben der sie sitzt.
  • Auf der JVM delegiert die Standardbibliothek bewusst nicht an java.util.Base64; die zwei Implementierungen sind getrennt, was das Verhalten auf allen Plattformen identisch hält, zum Preis einer auskommentierten Optimierung, die das Kotlin-Team im Baum für eine Zukunft gehalten hat, in der die Java-API es erlaubt.
  • Dasselbe 2.2-Release, das Base64 stabilisierte, stabilisierte auch HexFormat, die Hex-Formatierungsklasse in kotlin.text, die seit Kotlin 1.9 experimentell war, sodass textuelle Kodierungen auf Byte-Ebene jetzt einen festen Wohnsitz in der Standardbibliothek haben.

Zum Abschluss und wohin als Nächstes

Base64 in Kotlin zu produzieren kommt auf eine kurze Liste bewusster Entscheidungen an: Wählen Sie das Preset nach Kanal, entscheiden Sie Padding mit Absicht, halten Sie den Zeichensatz explizit, wenn die Eingabe Text ist, und respektieren Sie den 4/3-Aufschlag, wenn das Payload groß ist. Alles andere, Dateien, Buffer, Appendables, Streams, ist eine dünne Hülle um dieselben vier Instanzen. Die andere Richtung, diesen gepackten Text wieder in Bytes zurückzuholen, hat ihre eigenen Strenge-Regeln, ihre eigenen Fehlbetriebsmodi und ihre eigenen Fallen, und der verwandte Artikel auf der Schwesternsite behandelt die Base64-Dekodierung in Kotlin ausführlich.

Zuletzt aktualisiert: 2026-09-08

Verwandter Artikel: Base64-Dekodierung in Kotlin: Ein vollständiger Leitfaden