Ajouter la crate et encoder une chaîne
[dependencies]
base64 = "0.22" # the snippets below also build with 0.23
Depuis la 0.21, tout passe par un Engine. Le module prelude exporte le trait Engine et quatre engines prêts à l’emploi :
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 accepte tout ce qui implémente AsRef<[u8]> : &str, String, Vec<u8> et les littéraux d’octets fonctionnent tous. Les chaînes Rust sont en UTF-8, aucune étape de conversion n’est donc nécessaire. Si les octets décodés ne sont pas de l’UTF-8 valide, String::from_utf8 renvoie une erreur au lieu de deviner.
Correspondance entre les noms du prelude et base64::engine::general_purpose :
| Prelude | Engine | Alphabet | Remplissage à l’encodage | Remplissage au décodage |
|---|---|---|---|---|
BASE64_STANDARD | STANDARD | + / | oui | exigé |
BASE64_STANDARD_NO_PAD | STANDARD_NO_PAD | + / | non | doit être absent |
BASE64_URL_SAFE | URL_SAFE | - _ | oui | exigé |
BASE64_URL_SAFE_NO_PAD | URL_SAFE_NO_PAD | - _ | non | doit être absent |
Base64 URL-safe avec et sans remplissage
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"<<???>>"
Les engines sans remplissage rejettent une entrée qui en a (BASE64_URL_SAFE_NO_PAD.decode("PDw_Pz8-Pg==") renvoie Err(InvalidPadding)). Si vous acceptez des jetons de plusieurs producteurs, construisez un engine qui ne s’en soucie pas :
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
Les autres modes sont RequireCanonical (le défaut des engines avec remplissage) et RequireNone. Pour le contexte sur les variantes : Base64 ou Base64URL et le remplissage expliqué.
Encoder et décoder un fichier
let data = std::fs::read("hello.txt")?;
println!("{}", BASE64_STANDARD.encode(&data)); // aGVsbG8K
std::fs::write("copy.txt", BASE64_STANDARD.decode("aGVsbG8K")?)?;
Pour ajouter le résultat à un tampon existant sans nouvelle allocation, utilisez encode_string :
let mut s = String::from("Basic ");
BASE64_STANDARD.encode_string(b"user:pass", &mut s);
println!("{s}"); // Basic dXNlcjpwYXNz
Flux avec EncoderWriter et DecoderReader
Les modules write et read enveloppent les types de std::io : les gros fichiers ne restent jamais en mémoire.
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)?;
Appelez finish() explicitement. La destruction du writer tente aussi de vider le tampon, mais ne peut pas signaler d’erreur. Sur un fichier de test de 300 Ko, la sortie en flux était égale à BASE64_STANDARD.encode, et les octets décodés correspondaient à l’original.
Validation et DecodeError
decode renvoie Result<Vec<u8>, DecodeError>. Voici les résultats avec BASE64_STANDARD, sous forme de sortie Debug de la 0.22.1 et du message Display :
| Entrée | Variante | Message |
|---|---|---|
PDw_Pz8-Pg== | InvalidByte(3, 95) | Invalid symbol 95, offset 3. |
aGk | InvalidPadding | Invalid padding |
aGk=!! | InvalidByte(3, 61) | Invalid symbol 61, offset 3. |
a | InvalidLength(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. |
Les valeurs d’octets sont en décimal : 95 correspond à _, 61 à =, 10 à un saut de ligne. InvalidLastSymbol signale des bits de remplissage non nuls, que cette crate rejette par défaut ; with_decode_allow_trailing_bits(true) assouplit cette règle. En 0.23, la variante InvalidLastSymbol a changé et son message est désormais Invalid last symbol 0x6c ('l') at offset 2, decoded as 0b00100101. Faites le filtrage sur le nom de la variante, pas sur le texte.
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}"),
}
Collez une chaîne qui échoue dans le validateur Base64 pour voir le caractère fautif ; le guide des erreurs Base64 passe les causes en revue.
Pièges
- Les vieux tutoriels.
encode_config(x, base64::URL_SAFE)et les constantes de configuration datent d’avant la 0.21 et ne compilent plus ;base64::encode(x)compile encore, avec un avertissement d’obsolescence. Traduisez-les enURL_SAFE.encode(x)etSTANDARD.encode(x). - Oublier l’import du trait. Sans
use base64::Engine;(ou le prelude), appeler.encodesur un engine échoue avec « no method namedencodefound ». - Les espaces. Le décodeur traite les sauts de ligne et les espaces comme des octets invalides. Supprimez-les, par exemple avec
s.split_ascii_whitespace().collect::<String>(). - Pas d’aide pour MIME. La crate ne coupe pas les lignes. Découpez vous-même la sortie en morceaux de 76 caractères si vous produisez des corps d’e-mail.
Notes de version
- 0.21 : introduction de l’API Engine ; fonctions libres
encode/decoderendues obsolètes, fonctions*_configsupprimées. Les configurations sontGeneralPurposeConfigplusDecodePaddingMode. - 0.22 :
DecodeError::InvalidLengthdésigne désormais le nombre de symboles valides ;decode_slicefonctionne avec des tampons de taille exacte. - 0.23 : engines SIMD derrière la fonctionnalité par défaut
simd-unsafe, davantage de constantes préconfigurées, symboles de remplissage personnalisés, MSRV 1.71. Les engines généralistes utilisés sur cette page n’ont pas changé.
FAQ
Pourquoi base64::encode est-il marqué comme obsolète ?
Depuis la 0.21, les fonctions libres base64::encode et base64::decode sont des enveloppes obsolètes (toujours présentes en 0.23). Utilisez plutôt un engine : use base64::prelude::*; puis BASE64_STANDARD.encode(data).
Pourquoi la crate base64 rejette-t-elle une entrée sans remplissage ?
STANDARD exige un remplissage canonique. Utilisez URL_SAFE_NO_PAD ou STANDARD_NO_PAD pour une entrée sans remplissage, ou construisez un engine avec DecodePaddingMode::Indifferent pour accepter les deux.
La crate base64 ignore-t-elle les sauts de ligne ?
Non. "aGVs\nbG8K" échoue avec InvalidByte(4, 10). Supprimez les espaces avant de décoder des corps MIME ou PEM.