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

Base64 in Rust

Die Standardbibliothek von Rust hat kein Base64. Die De-facto-Wahl ist das Crate base64, dessen API mit 0.21 auf Engines umgestellt wurde. Die Snippets unten wurden gegen 0.23.1 und 0.22.1 gebaut.

Das Crate einbinden und einen String kodieren

[dependencies]
base64 = "0.22"   # the snippets below also build with 0.23

Seit 0.21 läuft alles über eine Engine. Das Modul prelude exportiert den Trait Engine und vier fertige Engines:

use base64::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let enc = BASE64_STANDARD.encode("héllo wörld");
    println!("{enc}");                              // aMOpbGxvIHfDtnJsZA==

    let bytes = BASE64_STANDARD.decode(&enc)?;
    println!("{}", String::from_utf8(bytes)?);      // héllo wörld
    Ok(())
}

encode akzeptiert alles, was AsRef<[u8]> ist, also funktionieren &str, String, Vec<u8> und Byte-Literale. Rust-Strings sind UTF-8, ein Umwandlungsschritt entfällt also. String::from_utf8 liefert einen Fehler, statt zu raten, wenn die dekodierten Bytes kein gültiges UTF-8 sind.

Die Namen aus dem Prelude entsprechen base64::engine::general_purpose:

PreludeEngineAlphabetPadding beim KodierenPadding beim Dekodieren
BASE64_STANDARDSTANDARD+ /jaerforderlich
BASE64_STANDARD_NO_PADSTANDARD_NO_PAD+ /neinmuss fehlen
BASE64_URL_SAFEURL_SAFE- _jaerforderlich
BASE64_URL_SAFE_NO_PADURL_SAFE_NO_PAD- _neinmuss fehlen

URL-sicheres Base64 mit und ohne Padding

let d = b"<<???>>";
BASE64_URL_SAFE.encode(d);          // "PDw_Pz8-Pg=="
BASE64_URL_SAFE_NO_PAD.encode(d);   // "PDw_Pz8-Pg"

let back = BASE64_URL_SAFE_NO_PAD.decode("PDw_Pz8-Pg")?;   // b"<<???>>"

Die No-Pad-Engines lehnen Eingaben mit Padding ab (BASE64_URL_SAFE_NO_PAD.decode("PDw_Pz8-Pg==") liefert Err(InvalidPadding)). Nehmen Sie Tokens von mehreren Erzeugern an, bauen Sie eine Engine, der das egal ist:

use base64::alphabet;
use base64::engine::general_purpose::{GeneralPurpose, GeneralPurposeConfig};
use base64::engine::DecodePaddingMode;

const URL_LENIENT: GeneralPurpose = GeneralPurpose::new(
    &alphabet::URL_SAFE,
    GeneralPurposeConfig::new()
        .with_encode_padding(false)
        .with_decode_padding_mode(DecodePaddingMode::Indifferent),
);

URL_LENIENT.decode("PDw_Pz8-Pg").is_ok();     // true
URL_LENIENT.decode("PDw_Pz8-Pg==").is_ok();   // true

Die anderen Modi sind RequireCanonical (Standard bei Engines mit Padding) und RequireNone. Hintergrund zu den Varianten: Base64 vs. Base64URL und Padding erklärt.

Eine Datei kodieren und dekodieren

let data = std::fs::read("hello.txt")?;
println!("{}", BASE64_STANDARD.encode(&data));          // aGVsbG8K

std::fs::write("copy.txt", BASE64_STANDARD.decode("aGVsbG8K")?)?;

Um ohne neue Allokation an einen vorhandenen Puffer anzuhängen, verwenden Sie encode_string:

let mut s = String::from("Basic ");
BASE64_STANDARD.encode_string(b"user:pass", &mut s);
println!("{s}");                                         // Basic dXNlcjpwYXNz

Streaming mit EncoderWriter und DecoderReader

Die Module write und read umhüllen std::io-Typen, große Dateien liegen also nie komplett im Speicher:

use std::fs::File;
use std::io::{self, Read};

let mut input = File::open("big.bin")?;
let mut w = base64::write::EncoderWriter::new(File::create("big.b64")?, &BASE64_STANDARD);
io::copy(&mut input, &mut w)?;
w.finish()?;            // writes the final block and padding

let mut r = base64::read::DecoderReader::new(File::open("big.b64")?, &BASE64_STANDARD);
let mut out = Vec::new();
r.read_to_end(&mut out)?;

Rufen Sie finish() ausdrücklich auf. Auch beim Drop versucht der Writer zu flushen, kann dabei aber keine Fehler melden. Bei einer 300-KB-Testdatei entsprach die gestreamte Ausgabe BASE64_STANDARD.encode, und die dekodierten Bytes stimmten mit dem Original überein.

Validierung und DecodeError

decode liefert Result<Vec<u8>, DecodeError>. Das sind die Ergebnisse mit BASE64_STANDARD, gezeigt als Debug-Ausgabe von 0.22.1 und als Display-Meldung:

EingabeVarianteMeldung
PDw_Pz8-Pg==InvalidByte(3, 95)Invalid symbol 95, offset 3.
aGkInvalidPaddingInvalid padding
aGk=!!InvalidByte(3, 61)Invalid symbol 61, offset 3.
aInvalidLength(1)Invalid input length: 1
aGl=InvalidLastSymbol(2, 108)Invalid last symbol 108, offset 2.
"aGVs\nbG8K"InvalidByte(4, 10)Invalid symbol 10, offset 4.

Bytewerte sind dezimal: 95 ist _, 61 ist =, 10 ist ein Zeilenumbruch. InvalidLastSymbol bedeutet Padding-Bits ungleich null, die dieses Crate standardmäßig ablehnt; with_decode_allow_trailing_bits(true) lockert das. In 0.23 hat sich die Variante InvalidLastSymbol geändert, ihre Meldung lautet jetzt Invalid last symbol 0x6c ('l') at offset 2, decoded as 0b00100101. Matchen Sie auf den Variantennamen, nicht auf den Text.

use base64::DecodeError;

match BASE64_STANDARD.decode(input) {
    Ok(bytes) => { /* use bytes */ }
    Err(DecodeError::InvalidByte(offset, byte)) => eprintln!("bad byte {byte} at {offset}"),
    Err(e) => eprintln!("invalid Base64: {e}"),
}

Fügen Sie einen fehlerhaften String in den Base64-Validator ein, um das schuldige Zeichen zu sehen; die Anleitung zu Base64-Fehlern geht die Ursachen durch.

Fallstricke

  • Alte Tutorials. encode_config(x, base64::URL_SAFE) und die Config-Konstanten stammen aus der Zeit vor 0.21 und kompilieren nicht mehr; base64::encode(x) baut noch, mit einer Deprecation-Warnung. Übersetzen Sie sie in URL_SAFE.encode(x) und STANDARD.encode(x).
  • Trait-Import vergessen. Ohne use base64::Engine; (oder das Prelude) scheitert der Aufruf von .encode auf einer Engine mit „no method named encode found“.
  • Leerraum. Der Decoder behandelt Zeilenumbrüche und Leerzeichen als ungültige Bytes. Entfernen Sie sie, zum Beispiel mit s.split_ascii_whitespace().collect::<String>().
  • Keine MIME-Hilfe. Das Crate bricht keine Zeilen um. Teilen Sie die Ausgabe selbst in Abschnitte zu 76 Zeichen, wenn Sie E-Mail-Inhalte erzeugen.

Versionshinweise

  • 0.21: Engine-API eingeführt; freie Funktionen encode/decode als veraltet markiert, *_config-Funktionen entfernt. Konfigurationen sind GeneralPurposeConfig plus DecodePaddingMode.
  • 0.22: DecodeError::InvalidLength bezieht sich jetzt auf die Anzahl gültiger Symbole; decode_slice funktioniert mit exakt bemessenen Puffern.
  • 0.23: SIMD-Engines hinter dem Standard-Feature simd-unsafe, mehr vorkonfigurierte Konstanten, eigene Padding-Symbole, MSRV 1.71. Die auf dieser Seite verwendeten General-Purpose-Engines sind unverändert.

Häufige Fragen

Warum ist base64::encode als veraltet markiert?

Seit 0.21 sind die freien Funktionen base64::encode und base64::decode veraltete Wrapper (in 0.23 noch vorhanden). Verwenden Sie stattdessen eine Engine: use base64::prelude::*; und dann BASE64_STANDARD.encode(data).

Warum lehnt das Crate base64 Eingaben ohne Padding ab?

STANDARD verlangt kanonisches Padding. Verwenden Sie URL_SAFE_NO_PAD oder STANDARD_NO_PAD für Eingaben ohne Padding, oder bauen Sie eine Engine mit DecodePaddingMode::Indifferent, die beides akzeptiert.

Überspringt das Crate base64 Zeilenumbrüche?

Nein. "aGVs\nbG8K" scheitert mit InvalidByte(4, 10). Entfernen Sie Leerraum, bevor Sie MIME- oder PEM-Inhalte dekodieren.

Weiterlesen