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 Dart: Ein vollständiger Leitfaden

Sie haben Bytes und brauchen einen String. Der Payload könnte eine Datei, ein Authentifizierungs-Credential, ein Konfigurations-Token oder ein Binär-Blob sein, der in einem JSON-Dokument mitfährt, und der Kanal akzeptiert nur Text. Base64 ist der Tausch, der das löst: Jeweils drei Eingabe-Bytes werden zu vier Zeichen aus einem Alphabet von 64 Zeichen, also ist die Ausgabe immer ein sauberes Vielfaches von vier und immer sicher in Text-only-Welten. Der Preis ist fest auf 33 Prozent mehr Zeichen, und das Format hängt am Ende ein oder zwei =-Padding-Zeichen an, wenn das letzte Stück zu kurz ist. Dieser Leitfaden ist das Dart-Rezept, diesen Tausch korrekt zu machen.

Nichts zu installieren. Base64 ist seit Dart 1.13 im Jahr 2015 in dart:convert dabei, und die API ist seitdem stabil; beide Alphabete, Standard und URL-sicher, sind seit über einem Jahrzehnt verfügbar. Die Startseite geht auf das Format im Detail ein; hier ist die Kodierer-Seite der Arbeit: die gesamte API-Oberfläche, die Bytes-zuerst-Disziplin, die den häufigsten Bug vermeidet, Padding- und Alphabet-Entscheidungen, und die realen Jobs: JWTs, Data URIs, Datei-Uploads, HTTP-Header, MIME, Konfiguration, Streams und die Kommandozeile. Dekodieren, die umgekehrte Richtung, hat ihren eigenen Leitfaden, verlinkt am Ende.

Ein Import, zwei Alphabete, eine Padding-Regel

Die gesamte öffentliche Oberfläche für das Kodieren lebt in dart:convert:

Eingang Alphabet Greifen Sie danach, wenn
base64Encode(bytes) Standard: A-Z a-z 0-9 + /, gepaddet APIs, MIME, Basic-Auth, die meisten Konsumenten
base64UrlEncode(bytes) URL-sicher: A-Z a-z 0-9 - _, immer noch gepaddet URLs, Dateinamen, JWTs, Objekt-IDs
base64.encode(bytes) Standard, identisch mit dem Top-Level-Aufruf Stream-Transformationen und Codec-Pipelines
Base64Encoder().convert(bytes) Standard wenn Sie eine benannte Encoder-Instanz benötigen

Zwei Regeln decken alle vier Zeilen ab. Erstens muss die Eingabe eine Liste von Byte-Werten sein, ganze Zahlen von 0 bis 255; alles andere, einschließlich negativer Werte oder 256 und mehr, wirft einen ArgumentError, der den falschen Index benennt. Zweitens ist die Ausgabe immer gepaddet: Es gibt keinen Schalter, Konstruktor oder eine Option, die ungepaddete Ausgabe produziert, denn das Padding des Formats ist eine Eigenschaft der Daten, und die Spezifikationen, die es nicht wollen, entfernen es als separaten, dokumentierten Schritt. Das kleinste mögliche Beispiel, von Anfang bis Ende:

import 'dart:convert';
void main() {
  final text = 'Dart is open source';
  final bytes = utf8.encode(text);
  final encoded = base64Encode(bytes);
  print(encoded); // RGFydCBpcyBvcGVuIHNvdXJjZQ==
}

Bytes zuerst: Die Reihenfolge, die Sie rettet

Der häufigste Dart-Base64-Bug hat überhaupt nichts mit Base64 zu tun. Es geht um die Reihenfolge der Operationen. Ein Dart-String ist eine Folge von UTF-16-Codeeinheiten, und base64Encode(text.codeUnits) zu rufen packt diese 16-Bit-Einheiten, nicht die Bytes, die der Empfänger erwartet. Bei reinem ASCII stimmen die beiden zufällig überein, deshalb versteckt sich der Bug bis der erste Akzentbuchstabe, das erste Emoji oder der erste CJK-Text ankommt. Dann verweigert der Kodierer die Arbeit, denn eine Codeeinheit wie 0x4e16 ist kein Byte-Wert:

import 'dart:convert';
void main() {
  final message = 'Héllo Wörld 世界';
  print(utf8.encode(message).length); // 20
  print(message.codeUnits.length); // 14
  print(base64Encode(utf8.encode(message)));
  try {
    base64Encode(message.codeUnits);
  } on ArgumentError catch (e) {
    print(e);
  }
}

Der ArgumentError weist auf den exakt fehlerhaften Index, also ist der Fehler laut statt still. Die Disziplin, die Sie beibehalten sollten: Entscheiden Sie, was die Bytes sind, bevor Sie mit Base64 reden. Text geht durch eine benannte Kodierung, utf8.encode für moderne Daten, und die resultierende List<int> ist das, was gepackt wird. Bytes aus einer Datei oder einem Netzwerk-Socket kommen bereits als Uint8List, was ohne jede Umwandlung die richtige Form für den Kodierer ist.

Padding: Der Job des Kodierers

Base64 bildet Gruppen von drei Bytes auf vier Zeichen ab, also hinterlässt ein Payload, dessen Länge keine Vielfache von drei ist, am Ende eine unvollständige Gruppe. Das Format markiert diesen Mangel mit =-Zeichen: Ein Eingabe-Byte wird zu vier Zeichen plus zwei Pads, zwei Bytes zu vier Zeichen plus einem Pad, drei Bytes zu exakt vier Zeichen. Darts Kodierer macht das für Sie, bedingungslos:

import 'dart:convert';
void main() {
  print(base64Encode([0x41])); // QQ==
  print(base64Encode([0x41, 0x42])); // QUI=
  print(base64Encode([0x41, 0x42, 0x43])); // QUJD
}

Dieses bedingungslose Verhalten ist ein Feature: Die Ausgabe ist immer ein legaler, selbstbeschreibender Base64-String. Wenn eine Spezifikation die ungepaddete Variante verlangt, und JWTs sind der übliche Grund, ist das Entfernen Ihr expliziter, sichtbarer Schritt, nicht eine Library-Einstellung:

base64UrlEncode(bytes).replaceAll('=', '')

Führen Sie das Entfernen dort aus, wo die Spezifikations-Grenze liegt, benennen Sie es und dokumentieren Sie es. Die Dekodierer-Seite dieses Tauschs, inklusive wie beschädigte oder entkleidete Eingabe repariert wird, ist im Dekodierungs-Leitfaden behandelt.

URL-sicheres Base64

Das Standardalphabet enthält +, / und =, und diese drei Zeichen kollidieren mit der URL-Syntax: Query-Trenner, Pfad-Trenner und Parameter-Grenzen. Das URL-sichere Alphabet, in RFC 4648 als base64url standardisiert, tauscht + gegen - und / gegen _ aus, so dass die Ausgabe in einem Pfadsegment, einem Query-Wert oder einem Dateinamen sitzen kann, ohne escapet zu werden. Hier ist der Unterschied an Bytes, die beide getauschten Zeichen ausreizen:

import 'dart:convert';
void main() {
  final tricky = [0xfb, 0xff, 0xfe, 0xf9];
  print(base64Encode(tricky)); // +//++Q==
  print(base64UrlEncode(tricky)); // -__--Q==
}

Wählen Sie nach dem Konsumenten, nicht nach Geschmack. Wenn der Wert in einer URL, einem JWT oder einem Dateinamen leben wird, kodieren Sie mit base64UrlEncode und entfernen Sie das Padding, wenn die Spezifikation ohne Padding ist. Wenn der Wert ein MIME-Körper, ein Basic-Auth-Header oder ein Feld in einem API-Vertrag sein wird, der "base64" sagt, verwenden Sie das Standardalphabet, denn base64 ohne Qualifikation bedeutet das Standard-Alphabet. Die beiden Alphabete sind in den Augen strikter Konsumenten nicht austauschbar: Ein Server, der Standard-Base64 erwartet, kann einen Payload mit - mit einem 400 und nichts Hilfreicherem ablehnen.

Zeichensätze: Welche Bytes packen Sie?

Wenn die Eingabe Text ist, entscheidet der Kodierungsschritt, welche Bytes Base64 sieht, und der Konsument nimmt auf der anderen Seite einen Zeichensatz an. Wenn Ihre Annahme und die des Konsumenten auseinanderliegen, ist die Ausgabe perfektes Base64 der falschen Bytes, die schlimmste Art von Bug, denn es wirft nichts. Für jeden modernen Austausch ist UTF-8 der Standard; die anderen Einzelbyte-Kodierungen existieren für Legacy-Daten:

Kodierung Verwenden Sie sie für Kodieren mit
utf8 Moderner Text, JSON, alles im Web utf8.encode(text)
latin1 Alte westliche Einzelbyte-Daten latin1.encode(text)
ascii Einfacher 7-Bit-Text ascii.encode(text)
import 'dart:convert';
void main() {
  final modern = base64Encode(utf8.encode('Héllo'));
  final legacy = base64Encode(latin1.encode('Héllo'));
  print(modern); // SMOpbGxv
  print(legacy); // SOlsbG8=
}

Dasselbe Wort, andere Bytes, anderes Base64. Achten Sie auf die Längen: UTF-8 braucht für Héllo sechs Bytes, denn der Akzent ist eine Zweibyten-Sequenz, während Latin-1 es in fünf unterbringt. Wenn der Konsument mit der Kodierung dekodiert, die Sie nicht verwendet haben, bekommt er Mojibake, und es wird so aussehen, als seien die Daten beim Transport beschädigt worden, dabei wurden sie schon bei der Absicht beschädigt.

JWTs: Den Token schreiben

Ein JSON Web Token besteht aus drei base64url-Teilen, verbunden durch Punkte: Header, Payload, Signatur. RFC 7515 nagelt zwei Details fest: Das Alphabet ist URL-sicher, und das Padding wird weggelassen, denn der Token ist dafür gebaut, in URLs und Headern zu sitzen. Die Signatur für den HS256-Algorithmus ist der HMAC-SHA256 von header.payload, selbst base64url ohne Padding. Mit dem crypto-Paket von Hand zu bauen dauert ein paar Zeilen, und es ist transparenter, als es aussieht:

import 'dart:convert';
import 'package:crypto/crypto.dart';
String base64UrlNoPadding(List<int> bytes) {
  return base64UrlEncode(bytes).replaceAll('=', '');
}
String createJwt(Map<String, dynamic> header, Map<String, dynamic> payload,
    List<int> secretKey) {
  final signingInput =
      '${base64UrlNoPadding(utf8.encode(jsonEncode(header)))}.'
      '${base64UrlNoPadding(utf8.encode(jsonEncode(payload)))}';
  final mac = Hmac(sha256, secretKey).convert(utf8.encode(signingInput));
  final signature = base64UrlNoPadding(mac.bytes);
  return '$signingInput.$signature';
}
void main() {
  final token = createJwt(
    {'alg': 'HS256', 'typ': 'JWT'},
    {'sub': 'user-42', 'exp': 1893456000},
    utf8.encode('a-32-byte-secret-key-0123456789'),
  );
  print(token);
}

Die Signatur wird über die exakten Bytes berechnet, die gepackt wurden, also solange Sie denselben String signieren, den Sie ausgeben, ist die Verifizierung auf der anderen Seite eine Wiederholung der gleichen Schritte. Drei Warnungen. Das alte jwt-Paket auf pub.dev ist von 2014 und vor der Null-Safety; die funktionierende Antwort des Ökosystems ist, mit crypto genau das zu tun, was hier gezeigt wurde. Geben Sie niemals einen Token mit alg: none aus, und lassen Sie niemals einen Client den Algorithmus wählen. Und denken Sie daran, dass der Payload von jedem lesbar ist, also nehmen Sie nur mit, was der Token bezeugen soll.

Data URIs: Dateien im Text versenden

Eine Data URI, definiert durch RFC 2397, ist eine URL, deren Payload die Daten selbst sind. Binärinhalte in einer Data URI werden base64-kodiert, deshalb taucht das Format überall auf, wo Textdokumente Bilder, Schriften oder Anhänge einbetten müssen: HTML-Attribute, CSS, JSON, Konfigurationsdateien. Dart kann die URIs nativ bauen, ohne URI-Paket:

import 'dart:convert';
import 'dart:io';
Future<void> main() async {
  final png = await File('icon.png').readAsBytes();
  final imageUri = Uri.dataFromBytes(png, mimeType: 'image/png');
  print(imageUri); // data:image/png;base64,iVBOR...
  final note = Uri.dataFromString('Hello, Dart!');
  print(note); // data:,Hello,%20Dart!
}

Uri.dataFromBytes base64-kodiert standardmäßig (es gibt eine percentEncoded: true-Option für die andere Form), was die richtige Kodierung für Binärdaten ist. Uri.dataFromString percent-kodiert standardmäßig, denn kurzer Text ist so kürzer, und akzeptiert ein base64: true-Flag, wenn Sie die gepackte Byte-Form wollen. Der praktische Stolperstein ist die Größe: Der Payload reist im Dokument mit, bei 33 Prozent Aufschlag, also sind Data URIs für kleine Assets da, Icons und Thumbnails, nicht, um Megabytes durch CSS zu schieben.

Dateien: Bytes für Textkanäle packen

Der Alltag-Job: Eine Datei, die durch JSON, eine Konfigurationsdatei oder jeden Text-only-Transport reisen muss. Das Muster lautet: Bytes lesen, kodieren, einbetten:

import 'dart:convert';
import 'dart:io';
Future<void> main() async {
  final image = await File('photo.jpg').readAsBytes();
  final encoded = base64Encode(image);
  final upload = jsonEncode({
    'name': 'photo.jpg',
    'size': image.length,
    'data': encoded,
  });
  print('payload ${upload.length} chars for ${image.length} bytes');
}

Die Zahl, die Sie im Kopf behalten sollten, ist das Wachstum: Eine 2.000-Byte-Datei wird zu 2.668 Base64-Zeichen, und ein bisschen mehr, sobald die JSON-Keys dazu kommen. Zwei Stolpersteine. Erstens: Prüfen Sie, dass Ihre Eingabe nicht bereits kodiert ist; ein bereits-base64-String zu base64-kodieren ist der klassische Doppel-Kodierungs-Bug, und er dekodiert "erfolgreich" zu einer weiteren Mauer aus Base64. Zweitens: Wenn der Kanal Binärdaten tragen kann, wofür multipart/form-data genau existiert, tragen Sie Binärdaten: Es ist ein Viertel kleiner, und die Base64-Abgabe ist reine Verschwendung.

HTTP und APIs: Header und Payloads

Der bekannteste Kodierungs-Job in HTTP ist der Authorization: Basic-Header: Das Wort Basic, ein Leerzeichen und das Base64 mit Standard-Alphabet von username:password:

import 'dart:convert';
import 'package:http/http.dart' as http;
Future<void> main() async {
  final credentials = base64Encode(utf8.encode('octocat:secret'));
  final client = http.Client();
  final response = await client.get(
    Uri.parse('https://httpbin.org/basic-auth/octocat/secret'),
    headers: {'Authorization': 'Basic $credentials'},
  );
  print(response.statusCode);
  client.close();
}

Mit dem http-Paket, ein dart pub add http entfernt, ist der Header einfach eine Zeichenfolge in der Anfrage. Der Stolperstein ist die Sicherheits-Einordnung: Base64 ist hier Verhüllung, kein Schutz. Jeder kann es in einem einzigen Schritt rückgängig machen, genau deshalb gehört Basic-Auth nur auf TLS-Verbindungen, wo nicht die Kodierung, sondern der Transport schützt. Für API-Payload-Felder folgen Sie dem Vertrag: Wenn dort base64 steht, ist das das Standard-Alphabet mit Padding, und die URL-sichere Variante ist etwas anderes, das strikte Konsumenten ablehnen.

E-Mail und MIME: Umbruch bei 76

MIME, das System, das E-Mails Binärdaten tragen lässt, verwendet Base64 als Content-Transfer-Encoding, und RFC 2045 schreibt vor, dass kodierte Zeilen nicht mehr als 76 Zeichen lang sein dürfen, mit CRLF dazwischen. Die Grenze ist eine MIME-Konvention - 76 plus CRLF passt bequem auf ein 80-Spalten-Display - und jeder konforme Kodierer bricht um. Darts Kodierer produziert einen ununterbrochenen String, also ist der Umbruch ein kurzer Nachbearbeitungs-Schritt:

import 'dart:convert';
String wrapForMime(String base64Text, [int lineLength = 76]) {
  final buffer = StringBuffer();
  for (var i = 0; i < base64Text.length; i += lineLength) {
    final end = i + lineLength > base64Text.length
        ? base64Text.length
        : i + lineLength;
    buffer
      ..write(base64Text.substring(i, end))
      ..write('\r\n');
  }
  return buffer.toString();
}
void main() {
  final encoded = base64Encode(utf8.encode('Hello from an email attachment'));
  print(wrapForMime(encoded));
}

Brechen Sie den fertigen String um, Padding inklusive, und lassen Sie die letzte Zeile so lang sein, wie sie ist, bis 76. Das eine, was Sie nicht tun sollten, ist, das Padding vor dem Umbruch zu entfernen in der Hoffnung, ein Zeichen zu sparen: Die Pads sind Teil des kodierten Inhalts, und ein Konsument, der die Zeilen neu zusammenfügt, wird das Ergebnis ohne sie ablehnen.

Konfiguration: Secrets als Einzeiler

Token, Schlüssel und Credentials, die Anführungszeichen, Zeilenumbrüche oder andere lästige Zeichen enthalten, werden manchmal base64-kodiert, damit sie sauber in eine Konfigurationszeile oder eine CI-Variable passen. Die ehrliche Einordnung zuerst: Das ist Verhüllung, keine Verschlüsselung, und alles, was je ein Repository oder ein Log erreicht, ist öffentlich. Verwenden Sie das Muster der Ordnung wegen, niemals der Geheimhaltung wegen. Den Wert zu kodieren ist ein Aufruf:

import 'dart:convert';
String forEnvFile(String secret) {
  return base64Encode(utf8.encode(secret));
}
void main() {
  final line = 'API_TOKEN_B64=${forEnvFile('sk-live-abc123')}';
  print(line); // API_TOKEN_B64=c2stbGl2ZS1hYmMxMjM=
}

Der Wert sitzt dann in einer .env-Datei, einem CI-Secret oder einem Compile-Zeit-Define und kommt nach einem Dekodieren als Klartext zurück. Wenn das Secret in Transit oder bei der Speicherung geschützt werden muss, greifen Sie zu einem Secret-Manager oder einer Verschlüsselungs-Bibliothek; die Aufgabe von Base64 hier ist es, die Textverarbeitung der Pipeline einfach zu halten, nicht mehr.

Streams: Kodierung über Chunk-Grenzen hinweg

Wenn die Bytes in Chunken ankommen, ein Netzwerk-Lesevorgang, eine Datei in Blöcken verarbeitet, kommt der Kodierer zurecht, ohne dass Sie etwas ausrichten müssen. Der Codec trägt die unvollständige Gruppe über Chunk-Grenzen hinweg, so dass die Chunk-Größen keine Vielfachen von drei sein müssen:

import 'dart:convert';
import 'dart:typed_data';
Future<void> main() async {
  final data = Uint8List(100000);
  for (var i = 0; i < data.length; i += 31) {
    data[i] = i % 256;
  }
  final chunks = <List<int>>[data.sublist(0, 777), data.sublist(777)];
  final encoded = await Stream.fromIterable(chunks)
      .transform(base64.encoder)
      .join();
  print('in: ${data.length}, out: ${encoded.length}'); // in: 100000, out: 133336
}

Zwei Chunken mit unbequemen Größen, 777 und 99.223 Bytes, produzieren einen einzigen korrekten 133.336-Zeichen-String, denn der Kodierer parkt die übrig gebliebenen Bits jeder unvollständigen Gruppe, bis der nächste Chunk ankommt, und gibt das Padding nur am Ende aus. Wenn Sie Sinks bevorzugen, gibt base64.encoder.startChunkedConversion Ihnen denselben Zustandsautomaten als ByteConversionSink (Sie füttern ihn mit Byte-Chunks, er gibt Strings aus), was der natürliche Fit ist, um große Ausgaben in eine Datei oder einen Socket zu schreiben, ohne jemals einen großen String zu joinen.

Big Data: Durchsatz und Speicher

Die Größen-Mathematik ist exakt und lohnt sich: Die Ausgabelänge ist die Eingangslänge, dividiert durch drei, aufgerundet, mal vier. Ein, zwei oder drei Bytes kosten alle vier Zeichen; ab dort ist es ein flacher 33-Prozent-Aufschlag. Die Formel, für den Fall, dass Sie Puffer reservieren oder Fortschritt melden müssen:

import 'dart:convert';
int encodedLength(int n) => (n + 2) ~/ 3 * 4;
void main() {
  print(encodedLength(100000)); // 133336
}

Geschwindigkeit ist nicht die Einschränkung; der Kodierer ist ein einzelner Durchlauf über eine Nachschlagetabelle, der Megabytes in Millisekunden verarbeitet. Die Einschränkungen sind die Größen-Abgabe selbst, berechnet auf dem Kabel und im Speicher, und die Tatsache, dass die kodierte Form ein String ist. Halten Sie beide im Blick, wenn es groß wird: Für Payloads, die groß werden können, streamen Sie die Kodierung wie oben gezeigt, statt eine große Liste und einen großen String zu sammeln, und bei wiederholtem Versand derselben Daten fragen Sie, ob der Kanal einen Binärmodus hat, denn 33 Prozent sind ein dauerhafter Aufschlag, den kein Algorithmus erstattet.

Der Kommandozeilen-Kodierer

Die VM macht aus dem Kodierer eine saubere CLI. Dieses Werkzeug liest ein Datei-Argument oder die Standard-Eingabe und gibt die Kodierung mit Standard-Alphabet aus:

import 'dart:convert';
import 'dart:io';
Future<void> main(List<String> args) async {
  final bytes = await _read(args);
  stdout.writeln(base64Encode(bytes));
}
Future<List<int>> _read(List<String> args) async {
  if (args.isNotEmpty) {
    return File(args[0]).readAsBytes();
  }
  final all = <int>[];
  await for (final chunk in stdin) {
    all.addAll(chunk);
  }
  return all;
}

Speichern Sie es als bin/encode.dart und führen Sie dart run bin/encode.dart photo.jpg > photo.b64 aus, oder pipen Sie es mit cat config | dart run bin/encode.dart. Der Begleiter, ein Dekodierer, der liest und glättet, ist das erste Beispiel im Dekodierungs-Leitfaden, und zusammen sind die beiden Skripte ein kleines, aber wirklich nützliches Werkzeug-Set, um Binärdaten durch Textkanäle zu bewegen.

Fallen, die auf dem Weg raus beißen

  • Die codeUnits-Falle. base64Encode(text.codeUnits) packt UTF-16-Einheiten, nicht Bytes; es funktioniert für ASCII und wirft ArgumentError bei der ersten Codeeinheit über 255. Kodieren Sie Text immer zuerst mit einer benannten Kodierung.
  • Alphabet-Mismatch. URL-sichere Ausgabe einem Konsumenten zu geben, der das Standardalphabet erwartet, ist ein 400, der ins Haus steht. Bestimmen Sie das Alphabet aus der Spezifikation, kodieren Sie einmal, und wandeln Sie nicht nachträglich um.
  • Padding-Annahmen. Dart paddet immer. Wenn die Spezifikation es ohne Padding will, entfernen Sie mit replaceAll('=', '') als expliziter Schritt an der Grenze, und sagen Sie es im Vertrag.
  • Zeichensatz-Drift. Latin-1-Bytes für einen Konsumenten zu kodieren, der UTF-8 dekodiert, produziert gültiges Base64 der falschen Daten. Es wirft nichts; der Text ist nur falsch.
  • Doppel-Kodierung. Einen Wert zu base64-kodieren, der bereits Base64 ist, ein Token, das aus einer anderen Config kopiert wurde, ist der klassische "dekodiert zu einer weiteren Mauer aus Base64"-Bug.
  • Privatsphäre-Illusion. Base64 ist ein Format, kein Chiffre. Wenn das Bedrohungsmodell einen Leser enthält, ist die Antwort Verschlüsselung, nicht Kodierung.
  • Veraltete Pakete. Das langjährig etablierte jwt-Paket auf pub.dev ist von vor der Null-Safety; für JWT-Arbeit ist crypto plus die paar Zeilen oben der gewartete Weg.

Wann Sie zu etwas anderem greifen sollten

  • Datei-Uploads über HTTP. Verwenden Sie multipart/form-data; es trägt rohe Bytes, so dass Sie die 33-Prozent-Abgabe ganz umgehen.
  • Große oder repetitive Payloads. Komprimieren Sie zuerst, kodieren Sie zweitens: Base64 von gezipptem Text ist dramatisch kleiner als Base64 vom Text selbst, und die Seite, die dekomprimiert, kennt das Format schon.
  • Kurzer Text in URLs. Prozent-Kodierung ist kürzer für ein paar Zeichen und hält den Wert lesbar für Menschen; Data URIs machen es sogar standardmäßig für Sie.
  • Debug-Ausgabe und Logs. Hex ist 50 Prozent länger als Base64 (doppelte Rohgröße, Base64 nur vier Drittel), aber viel einfacher zu scannen, zu diffen und einem Kollegen zu geben; für Binär-Schnipsel in Logs gewinnt es meistens.

Best Practices, die Liste des Kodierers

  • Bytes kodieren, niemals Codeeinheiten; Text geht zuerst durch eine benannte Kodierung.
  • Wählen Sie das Alphabet aus der Spezifikation des Konsumenten, bevor Sie den Aufruf schreiben.
  • Entfernen Sie Padding nur dort, wo die Spezifikation ohne Padding sagt, als sichtbarer Schritt an der Grenze.
  • Benennen Sie den Zeichensatz im Vertrag explizit; nehmen Sie nichts über die andere Seite an.
  • Streamen Sie alles, was groß werden kann.
  • Betrachten Sie base64 als Format für Text-only-Kanäle, niemals als Schutz für sensible Daten.

Eine kurze Geschichte von zwei Alphabeten

Das Format, das Sie gerade verwendet haben, ist älter als jede Dart-Veröffentlichung, und die Alphabet-Entscheidungen, die Ihnen zur Verfügung stehen, wurden Jahrzehnte vor Darts Ankunft standardisiert. Die Kurzversion:

  • 1993, RFC 1521: MIME führt Base64 als Content-Transfer-Encoding für E-Mail ein, mit dem Standard-Alphabet von 64 Zeichen und der 76-Zeichen-Grenze, an der dieser Artikel umbrechen lässt. Die Aufgabe des Formats, Binärdaten durch Textkanäle zu tragen, stammt von hier.
  • 1996, RFC 2045: Die MIME-Ablösung, die die Padding- und Zeilenlängen-Regeln von Base64 zum langlebigen Standard machte.
  • 2006, RFC 4648: Die Kodierung wird aus MIME herausgezogen und für sich selbst standardisiert, mit dem URL-sicheren Alphabet und dem Rat, dass Dekodierer ungültige Eingabe ablehnen sollten. Die Wahl zwischen zwei Alphabeten, die Sie in Dart bekommen, kommt aus diesem Dokument.
  • 2015, RFC 7515: JSON Web Signatures spezifizieren base64url ohne Padding, die Konvention hinter jedem JWT.
  • November 2015, Dart 1.13: base64 kommt in dart:convert dazu; die URL-sichere Variante folgt im Frühling darauf in Dart 1.16, und die Top-Level-base64Encode- und base64UrlEncode-Aufrufe, die Sie oben verwendet haben, landen in Dart 2.0 im Jahr 2018.
  • Heute, Dart 3.13: beide Alphabete, immer gepaddet, ein Import entfernt, dieselbe strenge und einfache Maschine seit 2015.

Der 33-Prozent-Aufschlag hat sich seit 1993 auch nicht verändert. Es ist eine Eigenschaft der Mathematik, vier Symbole für drei Bytes, und jede Implementierung, die Sie je verwenden werden, in jeder Sprache, zahlt sie identisch.

Spielerische Fakten von der Kodierbank

  • Der Kodierer kann nicht ausgeschaltet werden: Es gibt keinen Schalter für ungepaddete Ausgabe im SDK, deshalb ist "die Pads entfernen" immer Ihr Code, an Ihrer Grenze, in voller Sicht.
  • Ein Byte wird zu vier Zeichen: base64Encode([65]) ist QQ==. Der kürzeste mögliche Base64-String ist vier Zeichen lang, und nur die ersten zwei von ihnen tragen Information; die letzten zwei sind Padding.
  • Beide Dart-Kodierer padden, auch base64UrlEncode. Das "ohne Padding" bei base64url ist eine Konsumenten-Konvention aus RFC 7515, keine Eigenschaft des Alphabets.
  • Das Standardalphabet wurde als 7-Bit-druckbar entworfen, und es ist seitdem der Standard geblieben; die Tatsache, dass + und / schließlich URL-sichere Ersatzzeichen bekamen, ist ein Zeichen dafür, wie zentral es wurde, kein Makel.
  • Dieselben 20 UTF-8-Bytes von Héllo Wörld 世界 werden zu SMOpbGxvIFfDtnJsZCDkuJbnlYw= gepackt, während die 14 Codeeinheiten des Strings den Kodierer bei Index 12 zum Absturz brächten. Gleiche Zeichen, zwei komplett verschiedene Ausgaben, eine davon ein Fehler.
  • PEM-Dateien, die -----BEGIN CERTIFICATE------Blöcke in jedem TLS-Zertifikat, sind Base64, das mit Headern bei 64 Zeichen umgebrochen ist, und das Format stammt aus 1987, sechs Jahre bevor MIME Base64 für E-Mail veröffentlichte.

Sie haben jetzt die ganze Kodierer-Seite: die API-Oberfläche, die Bytes-zuerst-Disziplin, die Padding- und Alphabet-Entscheidungen, und die funktionierenden Muster für JWTs, Data URIs, Dateien, HTTP, MIME, Konfiguration, Streams und die Shell. Die umgekehrte Richtung, einen dieser Strings auseinander zu nehmen, mit der gesamten Strenge des Dekodierers, seiner Prozent-Escape-Überraschung und seinen Reparatur-Werkzeugen, ist im Base64-Dekodierungs-Leitfaden behandelt, direkt unten verlinkt.

Zuletzt aktualisiert: 2026-09-08

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