크레이트 추가와 문자열 인코딩
[dependencies]
base64 = "0.22" # the snippets below also build with 0.23
0.21부터 모든 작업은 Engine을 거칩니다. prelude 모듈은 Engine 트레이트와 미리 만들어진 엔진 네 개를 내보냅니다.
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는 AsRef<[u8]>인 것이면 무엇이든 받으므로 &str, String, Vec<u8>, 바이트 리터럴이 모두 동작합니다. Rust 문자열은 UTF-8이므로 변환 단계가 필요 없습니다. 디코딩한 바이트가 올바른 UTF-8이 아니면 String::from_utf8은 추측하는 대신 오류를 반환합니다.
prelude 이름은 base64::engine::general_purpose에 다음과 같이 대응합니다.
| prelude | 엔진 | 알파벳 | 인코딩 시 패딩 | 디코딩 시 패딩 |
|---|---|---|---|---|
BASE64_STANDARD | STANDARD | + / | 있음 | 필수 |
BASE64_STANDARD_NO_PAD | STANDARD_NO_PAD | + / | 없음 | 없어야 함 |
BASE64_URL_SAFE | URL_SAFE | - _ | 있음 | 필수 |
BASE64_URL_SAFE_NO_PAD | URL_SAFE_NO_PAD | - _ | 없음 | 없어야 함 |
패딩 있는 URL-safe Base64와 패딩 없는 URL-safe Base64
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"<<???>>"
패딩 없는 엔진은 패딩된 입력을 거부합니다(BASE64_URL_SAFE_NO_PAD.decode("PDw_Pz8-Pg==")는 Err(InvalidPadding)을 반환). 여러 생산자에게서 토큰을 받는다면 패딩을 신경 쓰지 않는 엔진을 만드세요.
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
다른 모드로는 RequireCanonical(패딩 엔진의 기본값)과 RequireNone이 있습니다. 변형에 대한 배경 지식: Base64와 Base64URL 비교, 패딩 설명.
파일 인코딩과 디코딩
let data = std::fs::read("hello.txt")?;
println!("{}", BASE64_STANDARD.encode(&data)); // aGVsbG8K
std::fs::write("copy.txt", BASE64_STANDARD.decode("aGVsbG8K")?)?;
새로 할당하지 않고 기존 버퍼에 덧붙이려면 encode_string을 사용하세요.
let mut s = String::from("Basic ");
BASE64_STANDARD.encode_string(b"user:pass", &mut s);
println!("{s}"); // Basic dXNlcjpwYXNz
EncoderWriter와 DecoderReader로 스트리밍
write와 read 모듈은 std::io 타입을 감싸므로 큰 파일이 메모리에 통째로 올라가지 않습니다.
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)?;
finish()를 명시적으로 호출하세요. writer를 drop해도 flush를 시도하지만 오류를 보고할 수 없습니다. 300 KB 테스트 파일에서 스트리밍 출력은 BASE64_STANDARD.encode와 같았고, 디코딩한 바이트도 원본과 일치했습니다.
검증과 DecodeError
decode는 Result<Vec<u8>, DecodeError>를 반환합니다. 아래는 BASE64_STANDARD로 얻은 결과로, 0.22.1의 Debug 출력과 Display 메시지를 함께 보여 줍니다.
| 입력 | 변형 | 메시지 |
|---|---|---|
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. |
바이트 값은 10진수입니다. 95는 _, 61은 =, 10은 줄 바꿈입니다. InvalidLastSymbol은 0이 아닌 패딩 비트를 뜻하며, 이 크레이트는 기본적으로 이를 거부합니다. with_decode_allow_trailing_bits(true)로 완화할 수 있습니다. 0.23에서는 InvalidLastSymbol 변형이 바뀌어 메시지가 Invalid last symbol 0x6c ('l') at offset 2, decoded as 0b00100101.로 나옵니다. 텍스트가 아니라 변형 이름으로 매칭하세요.
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}"),
}
실패하는 문자열을 Base64 검사기에 붙여넣으면 문제가 된 문자를 볼 수 있습니다. 원인은 Base64 오류 가이드에서 하나씩 살펴봅니다.
함정
- 오래된 튜토리얼.
encode_config(x, base64::URL_SAFE)와 config 상수는 0.21 이전 방식이라 더 이상 컴파일되지 않습니다.base64::encode(x)는 지원 중단 경고와 함께 아직 빌드됩니다. 각각URL_SAFE.encode(x)와STANDARD.encode(x)로 바꾸세요. - 트레이트 import 누락.
use base64::Engine;(또는 prelude)이 없으면 엔진에서.encode를 호출할 때 “no method namedencodefound"로 실패합니다. - 공백. 디코더는 줄 바꿈과 공백을 잘못된 바이트로 취급합니다. 예를 들어
s.split_ascii_whitespace().collect::<String>()으로 제거하세요. - MIME 도우미 없음. 이 크레이트는 줄 바꿈을 하지 않습니다. 이메일 본문을 만든다면 출력을 직접 76자 단위로 나누세요.
버전별 참고 사항
- 0.21: Engine API 도입.
encode/decode자유 함수는 지원 중단,*_config함수는 제거. config는GeneralPurposeConfig와DecodePaddingMode로 구성. - 0.22:
DecodeError::InvalidLength가 이제 유효한 기호의 개수를 가리킴.decode_slice가 정확한 크기의 버퍼에서 동작. - 0.23: 기본
simd-unsafe기능 뒤의 SIMD 엔진, 미리 구성된 상수 추가, 사용자 정의 패딩 기호, MSRV 1.71. 이 페이지에서 쓴 범용 엔진은 바뀌지 않았습니다.
자주 묻는 질문
base64::encode가 deprecated로 표시되는 이유는 무엇인가요?
0.21부터 자유 함수 base64::encode와 base64::decode는 지원 중단된 래퍼입니다(0.23에도 여전히 있음). 대신 엔진을 쓰세요. use base64::prelude::*; 다음에 BASE64_STANDARD.encode(data)를 호출하면 됩니다.
base64 크레이트가 패딩 없는 입력을 거부하는 이유는 무엇인가요?
STANDARD는 정규 패딩을 요구합니다. 패딩 없는 입력에는 URL_SAFE_NO_PAD나 STANDARD_NO_PAD를 쓰거나, DecodePaddingMode::Indifferent로 엔진을 만들어 둘 다 받으세요.
base64 크레이트는 줄 바꿈을 건너뛰나요?
아니요. "aGVs\nbG8K"는 InvalidByte(4, 10)으로 실패합니다. MIME이나 PEM 본문을 디코딩하기 전에 공백을 제거하세요.