Adicionar a crate e codificar uma string
[dependencies]
base64 = "0.22" # the snippets below also build with 0.23
Desde a 0.21, todo o trabalho passa por uma Engine. O módulo prelude exporta o trait Engine e quatro engines prontas:
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(())
}
O encode aceita qualquer coisa que seja AsRef<[u8]>, então &str, String, Vec<u8> e literais de bytes funcionam. As strings do Rust são UTF-8, então não há etapa de conversão. O String::from_utf8 retorna um erro em vez de adivinhar quando os bytes decodificados não são UTF-8 válido.
Os nomes do prelude correspondem a base64::engine::general_purpose:
| Prelude | Engine | Alfabeto | Preenchimento ao codificar | Preenchimento ao decodificar |
|---|---|---|---|---|
BASE64_STANDARD | STANDARD | + / | sim | obrigatório |
BASE64_STANDARD_NO_PAD | STANDARD_NO_PAD | + / | não | deve estar ausente |
BASE64_URL_SAFE | URL_SAFE | - _ | sim | obrigatório |
BASE64_URL_SAFE_NO_PAD | URL_SAFE_NO_PAD | - _ | não | deve estar ausente |
Base64 URL-safe com e sem preenchimento
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"<<???>>"
As engines sem preenchimento rejeitam entrada com preenchimento (BASE64_URL_SAFE_NO_PAD.decode("PDw_Pz8-Pg==") retorna Err(InvalidPadding)). Se você aceita tokens de vários geradores, crie uma engine indiferente a isso:
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
Os outros modos são RequireCanonical (o padrão das engines com preenchimento) e RequireNone. Contexto sobre as variantes: Base64 vs Base64URL e preenchimento explicado.
Codificar e decodificar um arquivo
let data = std::fs::read("hello.txt")?;
println!("{}", BASE64_STANDARD.encode(&data)); // aGVsbG8K
std::fs::write("copy.txt", BASE64_STANDARD.decode("aGVsbG8K")?)?;
Para anexar a um buffer existente sem nova alocação, use encode_string:
let mut s = String::from("Basic ");
BASE64_STANDARD.encode_string(b"user:pass", &mut s);
println!("{s}"); // Basic dXNlcjpwYXNz
Streaming com EncoderWriter e DecoderReader
Os módulos write e read envolvem tipos de std::io, então arquivos grandes nunca ficam inteiros na memória:
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)?;
Chame o finish() explicitamente. Descartar o writer também tenta fazer o flush, mas não consegue informar erros. Num arquivo de teste de 300 KB, a saída em streaming foi igual à do BASE64_STANDARD.encode, e os bytes decodificados bateram com o original.
Validação e DecodeError
O decode retorna Result<Vec<u8>, DecodeError>. Estes são os resultados com BASE64_STANDARD, mostrados como saída Debug da 0.22.1 e a mensagem Display:
| Entrada | Variante | Mensagem |
|---|---|---|
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. |
Os valores de byte estão em decimal: 95 é _, 61 é =, 10 é uma quebra de linha. InvalidLastSymbol indica bits de preenchimento diferentes de zero, que esta crate rejeita por padrão; with_decode_allow_trailing_bits(true) relaxa isso. Na 0.23, a variante InvalidLastSymbol mudou, e a mensagem agora diz Invalid last symbol 0x6c ('l') at offset 2, decoded as 0b00100101. Faça o match pelo nome da variante, não pelo texto.
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}"),
}
Cole uma string que falha no validador de Base64 para ver o caractere problemático; o guia de erros de Base64 percorre as causas.
Armadilhas
- Tutoriais antigos.
encode_config(x, base64::URL_SAFE)e as constantes de config são anteriores à 0.21 e não compilam mais;base64::encode(x)ainda compila, com um aviso de obsolescência. Traduza paraURL_SAFE.encode(x)eSTANDARD.encode(x). - Esquecer de importar o trait. Sem
use base64::Engine;(ou o prelude), chamar.encodenuma engine falha com “no method namedencodefound”. - Espaços em branco. O decodificador trata quebras de linha e espaços como bytes inválidos. Remova-os, por exemplo com
s.split_ascii_whitespace().collect::<String>(). - Sem helper de MIME. A crate não quebra linhas. Divida a saída em blocos de 76 caracteres você mesmo se gerar corpos de e-mail.
Notas de versão
- 0.21: introdução da API de Engine; as funções livres
encode/decodeficaram obsoletas e as funções*_configforam removidas. As configs passaram a serGeneralPurposeConfigmaisDecodePaddingMode. - 0.22:
DecodeError::InvalidLengthagora se refere ao número de símbolos válidos; odecode_slicefunciona com buffers de tamanho exato. - 0.23: engines SIMD por trás da feature padrão
simd-unsafe, mais constantes pré-configuradas, símbolos de preenchimento personalizados, MSRV 1.71. As engines de uso geral usadas nesta página não mudaram.
Perguntas frequentes
Por que o base64::encode está marcado como obsoleto?
Desde a 0.21, as funções livres base64::encode e base64::decode são wrappers obsoletos (ainda presentes na 0.23). Use uma engine: use base64::prelude::*; e depois BASE64_STANDARD.encode(data).
Por que a crate base64 rejeita entrada sem preenchimento?
A STANDARD exige preenchimento canônico. Use URL_SAFE_NO_PAD ou STANDARD_NO_PAD para entrada sem preenchimento, ou crie uma engine com DecodePaddingMode::Indifferent para aceitar as duas formas.
A crate base64 pula quebras de linha?
Não. "aGVs\nbG8K" falha com InvalidByte(4, 10). Remova os espaços em branco antes de decodificar corpos MIME ou PEM.