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 Sie brauchen einen String. Die Nutzlast könnte eine Datei, ein Authentifizierungs-Credential, ein Konfigurations-Token oder ein binärer Blob sein, der in einem JSON-Dokument reitet, 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 rein textbasierten Welten. Der Preis ist fest auf 33 Prozent mehr Zeichen angesetzt, und das Format fügt ein oder zwei =-Padding-Zeichen ans Ende hinzu, wenn das letzte Stück 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 das Format ausführlich durch; hier ist die Kodier-Seite der Arbeit: die komplette API-Oberfläche, die Bytes-erst-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 die Kodierung lebt in dart:convert:

Eintrag 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 zum Top-Level-Aufruf Stream-Transformationen und Codec-Pipelines
Base64Encoder().convert(bytes) Standard Sie eine benannte Kodierer-Instanz wollen

Zwei Regeln decken alle vier Zeilen ab. Erstens muss die Eingabe eine Liste von Byte-Werten sein, Ganzzahlen von 0 bis 255; alles andere, einschließlich Negativzahlen oder 256 und mehr, wirft einen ArgumentError, der den falschen Index nennt. Zweitens ist die Ausgabe immer gepaddet: Es gibt keine Flag, keinen Konstruktor und keine Option, die unpadded Ausgabe produziert, denn das Padding des Formats ist eine Eigenschaft der Daten, und die Spezifikationen, die es loswerden 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==
}

Erst die Bytes: 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 der Aufruf von base64Encode(text.codeUnits) 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 das erste akzentuierte Zeichen, Emoji oder CJK-Text eintrifft. 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 verweist auf den exakten fehlbaren Index, also ist der Fehler laut statt still. Die Disziplin, die man beibehalten sollte: Entscheiden Sie, was die Bytes sind, bevor Sie mit base64 reden. Text geht durch einen benannten Zeichensatz, 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 an, die genau die richtige Form für den Kodierer ist, ohne jede Konvertierung.

Padding: Die Aufgabe des Kodierers

Base64 bildet Gruppen von drei Bytes auf vier Zeichen ab, also lässt eine Nutzlast, deren Länge kein Vielfaches von drei ist, eine unvollständige Gruppe am Ende. Das Format markiert diesen Mangel mit =-Zeichen: Ein Eingabe-Byte wird zu vier Zeichen plus zwei Pads, zwei Bytes werden zu vier Zeichen plus einem Pad, drei Bytes werden zu genau 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, sich selbst beschreibender base64-String. Wenn eine Spezifikation die Variante ohne Padding verlangt, und JWTs sind der übliche Grund, dann ist das Entfernen Ihr expliziter, sichtbarer Schritt, keine Einstellung der Bibliothek:

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

Platzieren Sie das Entfernen dort, wo die Spezifikations-Grenze ist, benennen Sie es und dokumentieren Sie es. Die Dekodier-Seite dieses Tauschs, einschließlich wie beschädigte oder entpadete Eingaben repariert werden, ist im Dekodierungs-Leitfaden abgedeckt.

URL-sicheres Base64

Das Standard-Alphabet enthält +, / und =, und diese drei Zeichen kollidieren mit der URL-Syntax: Query-Trenner, Pfadtrenner und Parameter-Delimiter. Das URL-sichere Alphabet, standardisiert als base64url in RFC 4648, tauscht + gegen - und / gegen _ aus, also kann die Ausgabe in einem Pfad-Segment, einem Query-Wert oder einem Dateinamen sitzen, ohne Escape. Hier ist der Unterschied an Bytes, die beide getauschten Zeichen auslösen:

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 Standard-Alphabet, denn base64 ohne Qualifikation bedeutet das Standard-Alphabet. Die beiden Alphabete sind in den Augen strenger Konsumenten nicht austauschbar: Ein Server, der Standard-Base64 erwartet, kann eine Nutzlast, die - enthält, mit einem 400 und nichts Hilfreicherem ablehnen.

Zeichensätze: Welche Bytes packen Sie?

Wenn die Eingabe Text ist, entscheidet der Kodierungsschritt, welche Bytes base64 sehen wird, und der Konsument geht auf der anderen Seite von einem Zeichensatz aus. Wenn Ihre Annahme und die des Konsumenten auseinandergehen, ist die Ausgabe völlig gültiges base64 der falschen Bytes, die schlechteste Art von Bug, denn nichts wirft. Für jeden modernen Austausch ist UTF-8 der Standard; die anderen Single-Byte-Zeichensätze existieren für Legacy-Daten:

Zeichensatz Wofür Kodieren mit
utf8 Moderne Texte, JSON, alles aus dem Web utf8.encode(text)
latin1 Legacy-West-Single-Byte-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. Beachten Sie die Längen: UTF-8 braucht sechs Bytes für Héllo, denn der Akzent ist eine zwei-Byte-Sequenz, während Latin-1 ihn in fünf unterbringt. Wenn der Konsument mit dem Zeichensatz dekodiert, den Sie nicht verwendet haben, bekommt er Mojibake, und es sieht aus, als seien die Daten im Transport beschädigt worden, obwohl sie in Wirklichkeit von der Absicht her beschädigt sind.

JWTs: Den Token schreiben

Ein JSON Web Token besteht aus drei base64url-Teilen, verbunden durch Punkte: Header, Nutzlast, Signatur. RFC 7515 fixiert zwei Details: Das Alphabet ist URL-sicher, und das Padding wird weggelassen, denn das Token ist dafür gebaut, in URLs und Headern zu sitzen. Die Signatur für den HS256-Algorithmus ist das HMAC-SHA256 von header.payload, selbst base64url ohne Padding. Es mit dem crypto-Paket selbst zu rollen 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 ist die Verifizierung auf der anderen Seite eine Wiederholung derselben Schritte, solange Sie denselben String signieren, den Sie ausgeben. Drei Warnungen. Das alte jwt-Paket auf pub.dev ist von 2014 und älter als Null Safety; die funktionierende Antwort des Ökosystems ist, mit crypto zu tun, was hier gezeigt wird. Emittieren Sie niemals ein Token mit alg: none, und lassen Sie niemals einen Client den Algorithmus wählen. Und denken Sie daran, dass die Nutzlast von jedem lesbar ist, also enthalten Sie nur das, was das Token nachweisen soll.

Data URIs: Dateien im Text transportieren

Eine Data URI, definiert durch RFC 2397, ist eine URL, deren Nutzlast die Daten selbst sind. Binärer Inhalt in einer Data URI ist base64-kodiert, deshalb taucht das Format überall dort 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 kodiert standardmäßig base64 (es hat ein percentEncoded: true-Opt-in für die andere Form), die korrekte Kodierung für Binärdaten. Uri.dataFromString kodiert standardmäßig prozentkodiert, denn kurzer Text ist so kürzer, und akzeptiert eine base64: true-Flag, wenn Sie die bytes-verpackte Form wollen. Die praktische Falle ist die Größe: Die Nutzlast reitet im Dokument mit, mit 33 Prozent Overhead, also sind Data URIs für kleine Assets, Icons und Thumbnails, nicht für das Verschiffen von Megabytes durch CSS.

Dateien: Bytes für Textkanäle packen

Der Alltags-Job: eine Datei, die durch JSON, eine Konfigurationsdatei oder jeden rein textbasierten Transport wandern 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 Fallen. Erstens, prüfen Sie, dass Ihre Eingabe nicht bereits kodiert ist: einen bereits base64-kodierten String zu base64-kodieren ist der klassische Double-Encode-Bug, und er dekodiert "erfolgreich" zu einer weiteren Wand aus base64. Zweitens, wenn der Kanal Binärdaten tragen kann, wofür multipart/form-data ja 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 Kodierungsjob in HTTP ist der Authorization: Basic-Header: das Wort Basic, ein Leerzeichen und das Standard-Alphabet-base64 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 ein String in der Anfrage. Die Falle ist der Sicherheitsrahmen: base64 hier ist Obskurierung, kein Schutz. Jeder kann es in einem Schritt rückgängig machen, genau deshalb gehört Basic-Auth nur auf TLS-Verbindungen, wo der Transport, nicht die Kodierung, den Schutz übernimmt. Für API-Nutzlast-Felder folgen Sie dem Vertrag: Wenn er base64 sagt, dann ist das das Standard-Alphabet mit Padding, und die URL-sichere Variante ist etwas anderes, das strenge Konsumenten ablehnen werden.

E-Mail und MIME: Umbruch bei 76

MIME, das System, das E-Mail Binärdaten tragen lässt, verwendet base64 als Content-Transfer-Kodierung, und RFC 2045 legt fest, dass kodierte Zeilen nicht länger als 76 Zeichen sein dürfen, mit CRLF dazwischen. Die Grenze ist eine MIME-Konvention - 76 plus CRLF passt bequem auf eine 80-spaltige Anzeige - und jeder konforme Kodierer bricht um. Darts Kodierer produziert einen ununterbrochenen String, also ist der Umbruch ein kurzer Nachbearbeitungsschritt:

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 Einzige, was man nicht tun sollte, 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 wieder zusammensetzt, wird das Ergebnis ohne sie ablehnen.

Konfiguration: Secrets auf eine Zeile bringen

Token, Schlüssel und Zugangsdaten, die Anführungszeichen, Zeilenumbrüche oder andere unbequeme Zeichen enthalten, werden manchmal base64-kodiert, damit sie sauber in eine Konfigurationszeile oder eine CI-Variable passen. Zuerst der ehrliche Rahmen: Das ist Obskurierung, keine Verschlüsselung, und alles, was jemals ein Repository oder ein Log erreicht, ist öffentlich. Verwenden Sie das Muster für Ordnung, nie für Geheimhaltung. 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-Time-Define und kommt nach einem einzigen Dekodieren als Klartext zurück. Wenn das Secret im Transit oder im Ruhezustand geschützt werden muss, greifen Sie zu einem Secret-Manager oder einer Verschlüsselungs-Bibliothek; die Aufgabe von base64 hier ist, die Textverarbeitung der Pipeline einfach zu halten, nichts mehr.

Streams: Kodierung über Chunk-Grenzen hinweg

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

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 unbequem große Chunks, 777 und 99.223 Bytes, produzieren einen korrekten 133.336-Zeichen-String, denn der Kodierer parkt die übrig gebliebenen Bits jeder unvollständigen Gruppe, bis das nächste Chunk ankommt, und gibt das Padding nur am Ende aus. Wenn Sie Sinks bevorzugen, gibt Ihnen base64.encoder.startChunkedConversion dieselbe Zustandsmaschine als ByteConversionSink (Sie füttern sie mit Byte-Chunks, sie 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 verbinden.

Big Data: Durchsatz und Speicher

Die Größen-Mathematik ist exakt und lohnt sich zum Merken: Die Ausgabelänge ist die Eingabelänge, dividiert durch drei, aufgerundet, mal vier. Ein, zwei oder drei Bytes kosten alle vier Zeichen; ab dort ist es ein flacher 33-Prozent-Overhead. Die Formel, für wenn 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 Tabellen-Nachschlags-Pass, der Megabytes in Millisekunden verarbeitet. Die Einschränkungen sind die Größen-Abgabe selbst, berechnet auf dem Draht und im Speicher, und die Tatsache, dass die kodierte Form ein String ist. Denken Sie bei großer Skalierung an beides: Für Nutzlasten, die groß werden können, streamen Sie die Kodierung wie oben gezeigt, statt eine große Liste und einen großen String anzuhäufen, und bei wiederholtem Transfer derselben Daten fragen Sie, ob der Kanal einen Binär-Modus hat, denn 33 Prozent sind ein dauerhafter Zuschlag, den kein Algorithmus erstattet.

Der Kodierer für die Kommandozeile

Die VM macht einen sauberen CLI aus dem Kodierer. Dieses Tool liest ein Datei-Argument oder die Standardeingabe und druckt die Standard-Alphabet-Kodierung:

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 leiten Sie es um mit cat config | dart run bin/encode.dart. Der Begleiter, ein Dekodierer, der liest und alle Umbrüche entfernt, ist das erste Beispiel im Dekodierungs-Leitfaden, und zusammen sind die zwei Skripte ein kleines, aber genuinely nützliches Werkzeugset, um Binärdaten durch Textkanäle zu bewegen.

Fallen, die auf dem Weg nach draußen 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 einem benannten Zeichensatz.
  • Alphabet-Mismatch. URL-sichere Ausgabe an einen Konsumenten zu füttern, der das Standard-Alphabet erwartet, ist ein 400, der darauf wartet, zu passieren. Entscheiden Sie das Alphabet aus der Spezifikation, kodieren Sie einmal und konvertieren Sie nicht nachträglich.
  • Padding-Annahmen. Dart paddet immer. Wenn die Spezifikation ohne Padding will, entfernen Sie mit replaceAll('=', '') als expliziten Schritt an der Grenze, und sagen Sie das im Vertrag.
  • Zeichensatz-Drift. Latin-1-Bytes für einen Konsumenten zu kodieren, der UTF-8 dekodiert, produziert gültiges base64 der falschen Daten. Nichts wirft; der Text ist einfach falsch.
  • Doppel-Kodierung. Einen Wert, der bereits base64 ist, zu base64-kodieren, ein Token, das aus einer anderen Konfiguration kopiert wurde, ist der klassische "dekodiert zu einer weiteren Wand 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ährige jwt-Paket auf pub.dev ist älter als Null Safety; für JWT-Arbeit ist crypto plus die wenigen 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, also sparen Sie die 33-Prozent-Abgabe komplett.
  • Große oder repetitive Nutzlasten. Komprimieren Sie zuerst, kodieren Sie zweitens: base64 von gzip-pem Text ist dramatisch kleiner als base64 vom Text, und die entkomprimierende Seite kennt das Format bereits.
  • Kurzer Text in URLs. Prozent-Kodierung ist kürzer für ein paar Zeichen und hält den Wert für Menschen lesbar; Data URIs tun es sogar standardmäßig für Sie.
  • Debug-Ausgabe und Logs. Hex ist 50 Prozent länger als base64 (doppelt die Rohgröße, base64 nur vier Drittel), aber deutlich leichter zu scannen, zu diffen und einem Kollegen zu reichen; für Binär-Snippets in Logs gewinnt es normalerweise.

Best Practices, die Liste des Kodierers

  • Kodieren Sie Bytes, niemals Codeeinheiten; Text geht zuerst durch einen benannten Zeichensatz.
  • 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 sichtbaren Schritt an der Grenze.
  • Nennen Sie den Zeichensatz explizit im Vertrag; nehmen Sie nichts über die andere Seite an.
  • Streamen Sie alles, was groß werden kann.
  • Behandeln Sie base64 als Format für rein textbasierte Kanäle, nie als Schutz für sensible Daten.

Eine kurze Geschichte zweier Alphabete

Das Format, das Sie gerade verwendet haben, ist älter als jedes Dart-Release, und die Alphabet-Entscheidungen, die Ihnen zur Verfügung stehen, wurden Jahrzehnte vor Darts Ankunft standardisiert. Die kurze Version:

  • 1993, RFC 1521: MIME führt base64 als Content-Transfer-Kodierung für E-Mail ein, mit dem 64-Zeichen-Standard-Alphabet und der 76-Zeichen-Zeilen-Grenze, bei der dieser Artikel umbreicht. Die Aufgabe des Formats, Binärdaten durch Textkanäle zu tragen, stammt von hier.
  • 1996, RFC 2045: Die MIME-Obsoletierung, die die Padding- und Zeilenlängen-Regeln von base64 zum dauerhaften Standard machte.
  • 2006, RFC 4648: Die Kodierung wird aus MIME herausgelöst und für sich standardisiert, ergänzt um das URL-sichere Alphabet und den Rat, dass Dekodierer ungültige Eingaben ablehnen sollen. Die Zwei-Alphabet-Wahl, die Sie in Dart bekommen, stammt 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; die URL-sichere Variante folgt im nächsten Frühling 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-Overhead hat sich seit 1993 auch nicht geändert. Er ist eine Eigenschaft der Mathematik, vier Symbole für drei Bytes, und jede Implementation, die Sie je verwenden werden, in jeder Sprache, zahlt sie identisch.

Fun-Fakten vom Kodierprüfstand

  • Der Kodierer kann nicht ausgeschaltet werden: Es gibt im SDK keine Flag für unpadded Ausgabe, deshalb ist "die Pads entfernen" immer Ihr Code, an Ihrer Grenze, in vollem Blickfeld.
  • 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, sogar base64UrlEncode. Das "ohne Padding" in base64url ist eine Konsumenten-Konvention aus RFC 7515, keine Eigenschaft des Alphabets.
  • Das Standard-Alphabet wurde dafür designed, 7-Bit-druckbar zu sein, und es ist seither der Standard geblieben; die Tatsache, dass + und / schließlich URL-sichere Ersatzzeichen erhalten haben, ist ein Zeichen dafür, wie zentral es wurde, kein Makel.
  • Dieselben 20 UTF-8-Bytes von Héllo Wörld 世界 packen sich zu SMOpbGxvIFfDtnJsZCDkuJbnlYw=, während die 14 Codeeinheiten des Strings den Kodierer bei Index 12 zum Absturz bringen würden. Dieselben Zeichen, zwei völlig verschiedene Ausgaben, eine davon ein Fehler.
  • PEM-Dateien, die -----BEGIN CERTIFICATE------Blöcke in jedem TLS-Zertifikat, sind base64, bei 64 Zeichen umgebrochen mit Headern, und das Format stammt aus 1987, sechs Jahre bevor MIME base64 für E-Mail veröffentlichte.

Sie haben jetzt die ganze Kodier-Seite: die API-Oberfläche, die Bytes-erst-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 auseinanderzunehmen, mit all der Strenge des Dekodierers, seiner Prozent-Escape-Überraschung und seinen Reparatur-Tools, ist im Base64-Dekodierungs-Leitfaden abgedeckt, verlinkt direkt unten.

Zuletzt aktualisiert: 2026-09-08

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