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

Base64 in Dart

Die Bibliothek dart:convert bringt Base64-Codecs für beide Alphabete mit, dazu eine Methode normalize, die das Padding repariert. Die Ausgaben unten stammen von Dart 3.13.

Einen String kodieren und dekodieren

Dart-Strings sind intern UTF-16, wandeln Sie sie also vor dem Kodieren mit utf8.encode in UTF-8-Bytes um:

import 'dart:convert';

void main() {
  final enc = base64Encode(utf8.encode('héllo wörld'));
  print(enc);                            // aMOpbGxvIHfDtnJsZA==
  print(utf8.decode(base64Decode(enc))); // héllo wörld
}

base64Encode(bytes) ist die Kurzform von base64.encode(bytes), und base64Decode(s) die von base64.decode(s). Sind die dekodierten Bytes kein UTF-8, wirft utf8.decode die Meldung FormatException: Invalid UTF-8 byte; mit allowMalformed: true erhalten Sie stattdessen Ersatzzeichen.

Dieselben beiden Aufrufe erzeugen einen HTTP-Basic-Auth-Header, zum Beispiel für package:http:

final headers = {'Authorization': 'Basic ${base64Encode(utf8.encode('user:pass'))}'};
// Authorization: Basic dXNlcjpwYXNz

Der Basic-Auth-Header-Generator zeigt denselben Wert für beliebige Zugangsdaten.

URL-sicheres Base64 und normalize()

base64Url ist ein zweiter Codec mit dem Alphabet -_. Beide Encoder schreiben immer Padding; entfernen Sie es, wenn Ihr Format keines will:

final d = utf8.encode('<<???>>');
print(base64Url.encode(d));                     // PDw_Pz8-Pg==
print(base64Url.encode(d).replaceAll('=', '')); // PDw_Pz8-Pg

Beim Dekodieren ist Dart freundlicher als die meisten Sprachen. Beide Decoder akzeptieren beide Alphabete, verlangen aber korrektes Padding. normalize behebt das und wandelt außerdem das Alphabet um:

base64Url.normalize('PDw_Pz8-Pg');   // PDw_Pz8-Pg==
base64.normalize('PDw_Pz8-Pg');      // PDw/Pz8+Pg==

utf8.decode(base64Url.decode(base64Url.normalize('PDw_Pz8-Pg')));  // <<???>>
utf8.decode(base64Decode('PDw_Pz8-Pg=='));                         // <<???>>

normalize wirft außerdem eine FormatException, wenn die Eingabe kein gültiges Base64 ist, und taugt damit auch als Validator. JWT-Segmente sind Base64URL ohne Padding; siehe Base64 vs. Base64URL und Padding erklärt, oder fügen Sie eines in den JWT-Decoder ein.

Mit normalize lässt sich die Payload eines JWT in einer Zeile lesen:

const jwt = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.sig';
final payload = utf8.decode(base64Url.decode(base64Url.normalize(jwt.split('.')[1])));
print(payload);                        // {"sub":"1234"}
print(jsonDecode(payload)['sub']);     // 1234

Das liest nur die Claims und prüft die Signatur nicht. Verlassen Sie sich auf dem Client also nie für die Autorisierung darauf.

Eine Datei kodieren und dekodieren

import 'dart:io';

final bytes = await File('hello.txt').readAsBytes();
print(base64Encode(bytes));                              // aGVsbG8K
await File('copy.txt').writeAsBytes(base64Decode('aGVsbG8K'));

Große Dateien streamen

base64.encoder und base64.decoder sind Stream-Transformer, eine Datei lässt sich also Block für Block umwandeln:

// encode: bytes -> Base64 text -> ASCII bytes on disk
await File('big.bin')
    .openRead()
    .transform(base64.encoder)
    .transform(ascii.encoder)
    .pipe(File('big.b64').openWrite());

// decode: ASCII bytes -> String chunks -> bytes
await File('big.b64')
    .openRead()
    .transform(ascii.decoder)
    .transform(base64.decoder)
    .pipe(File('big.out').openWrite());

Die Transformer heben übrig gebliebene Bytes zwischen den Blöcken auf, die Blockgröße spielt also keine Rolle. In beiden Richtungen kam eine 300-KB-Testdatei Byte für Byte genauso heraus wie mit den Funktionen im Speicher.

Flutter: Bilder und Data-URIs

So zeigen Sie ein Base64-Bild an:

Image.memory(base64Decode(b64))

Um Base64 aus einem Asset oder einer ausgewählten Datei zu erzeugen, lesen Sie die Bytes und rufen base64Encode auf. Bei Data-URIs kümmert sich UriData um das Präfix:

UriData.parse('data:text/plain;base64,aGk=').contentAsBytes();  // [104, 105]
UriData.fromBytes(pngBytes, mimeType: 'image/png').toString();   // data:image/png;base64,...

Eine Data-URI direkt an base64Decode zu übergeben scheitert mit Invalid character (at character 5); gemeint ist der : nach data. Um zu sehen, was ein unbekannter String enthält, zeigt Base64 in Bild eine Vorschau im Browser.

Validierung und FormatException-Meldungen

Alle Dekodierfehler sind FormatException. toString() enthält die Meldung, die Eingabe und ein Caret unter der Problemstelle; e.message und e.offset liefern die Teile einzeln. Mit base64Decode:

EingabeMeldung
aGkInvalid length, must be multiple of four (at character 4)
aInvalid length, must be multiple of four (at character 2)
aGk=!!Invalid padding character (at character 5)
aGk==Invalid padding character (at character 5)
aGl=Invalid encoding before padding (at character 4)
"aGVs\nbG8K"Invalid character (at character 5)

In zwei Punkten ist Dart streng, wo viele Sprachen tolerant sind: Es lehnt Zeilenumbrüche ab (entfernen Sie sie bei MIME- oder PEM-Eingaben) und Padding-Bits ungleich null (aGl=). Eine Gültigkeitsprüfung:

bool isBase64(String s) {
  try {
    base64Decode(s);
    return true;
  } on FormatException {
    return false;
  }
}

Scheitert ein String und sagt die Meldung nicht genug, markiert der Base64-Validator das Problem; Häufige Base64-Fehler erklärt die Ursachen.

Fallstricke

  • codeUnits kodieren. base64Encode('é'.codeUnits) liefert 6Q== (Latin-1, nicht UTF-8), und '✓'.codeUnits wirft ArgumentError: Invalid argument (Not a byte value at index 0: 0x2713). Gehen Sie immer über utf8.encode.
  • Zeilenumbrüche aus anderen Systemen. MIME-Ausgaben aus E-Mails oder von base64 unter Linux enthalten \n; entfernen Sie Leerraum vorher mit s.replaceAll(RegExp(r'\s'), '').
  • URL-sichere Ausgabe von base64Encode erwarten. Es schreibt + und /. Für alles, was in eine URL kommt, verwenden Sie base64Url.encode.
  • Kein MIME-Umbruch. Die Codecs fügen nie Zeilenumbrüche ein. Teilen Sie bei Bedarf selbst in Zeilen zu 76 Zeichen.

Häufige Fragen

Wie zeige ich ein Base64-Bild in Flutter an?

Dekodieren Sie in Bytes und verwenden Sie Image.memory(base64Decode(b64)). Ist der String eine Data-URI, entfernen Sie zuerst alles bis zum Komma oder nutzen UriData.parse(uri).contentAsBytes().

Warum meldet base64Decode „Invalid length, must be multiple of four“?

Die Eingabe hat kein Padding, was für JWTs und URL-sichere Tokens typisch ist. Rufen Sie zuerst base64.normalize(s) oder base64Url.normalize(s) auf; das ergänzt die fehlenden =.

Akzeptiert base64Decode in Dart URL-sichere Eingaben?

Ja. Der Decoder akzeptiert sowohl +/ als auch -_, base64Decode('PDw_Pz8-Pg==') funktioniert also. Nur die Encoder unterscheiden sich: base64 schreibt +/, base64Url schreibt -_.

Weiterlesen