Base64是一種能將任意Binary資料用64種字元組合成字串的方法,而這個Binary資料和字串資料彼此之間是可以互相轉換的,十分方便。Base64通常會用在小量資料上,因此可以完全將資料放在主記憶體中處理。但如果遇到大量資料時該怎麼辦呢?
Base64的編碼方式,會將3個位元組的資料編碼成4個位元組,也就是說,如果要一次編碼一個#{{X}}#大小的資料,那麼就會需要使用到#{{ X + { {4 \over 3}X } = {7 \over 3}X }}#的記憶體空間。假設#{{X}}#為30MB,就需要70MB的記憶體空間來進行編碼!
因此,用Base64編碼大量資料時,最好還是將資料切割讀取,分段進行編碼。當然,Base64解碼也會遇到記憶體空間的問題,而需要做分段解碼。
Base64 Stream
「Base64 Stream」是筆者開發的套件,提供了ToBase64Reader
、ToBase64Writer
、FromBase64Reader
、FromBase64Writer
這四樣工具,分別實作了Read
和Write
特性,可以進行全方位的Base64編解碼。
Crates.io
Cargo.toml
使用方法
ToBase64Reader
使用use
關鍵字來將base64_stream
這個crate底下的ToBase64Reader
結構體給引用到當前的程式範圍下。ToBase64Reader
結構體實作了Read
特性,可以從原始的資料來源中,讀取出Base64編碼後的資料。
use std::io::{Cursor, Read};
use base64_stream::ToBase64Reader;
let test_data = b"Hi there, this is a simple sentence used for testing this crate. I hope all cases are correct.".to_vec();
let mut reader = ToBase64Reader::new(Cursor::new(test_data));
let mut base64 = String::new();
reader.read_to_string(&mut base64).unwrap();
assert_eq!("SGkgdGhlcmUsIHRoaXMgaXMgYSBzaW1wbGUgc2VudGVuY2UgdXNlZCBmb3IgdGVzdGluZyB0aGlzIGNyYXRlLiBJIGhvcGUgYWxsIGNhc2VzIGFyZSBjb3JyZWN0Lg==", base64);
ToBase64Writer
使用use
關鍵字來將base64_stream
這個crate底下的ToBase64Writer
結構體給引用到當前的程式範圍下。ToBase64Writer
結構體實作了Write
特性,可以輸入原始的資料,並輸出Base64編碼後的資料。
use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
use base64_stream::ToBase64Writer;
const DATA_FOLDER: &str = "data";
const ENCODE_OUTPUT: &str = "encode_output.txt";
let test_data = b"Hi there, this is a simple sentence used for testing this crate. I hope all cases are correct.".as_ref();
let file_path = Path::new("tests").join(DATA_FOLDER).join(ENCODE_OUTPUT);
let base64 = File::create(file_path.as_path()).unwrap();
let mut writer = ToBase64Writer::new(base64);
writer.write_all(test_data).unwrap();
writer.flush().unwrap(); // the flush method is only used when the full plain data has been written
assert_eq!("SGkgdGhlcmUsIHRoaXMgaXMgYSBzaW1wbGUgc2VudGVuY2UgdXNlZCBmb3IgdGVzdGluZyB0aGlzIGNyYXRlLiBJIGhvcGUgYWxsIGNhc2VzIGFyZSBjb3JyZWN0Lg==", fs::read_to_string(file_path).unwrap());
FromBase64Reader
使用use
關鍵字來將base64_stream
這個crate底下的FromBase64Reader
結構體給引用到當前的程式範圍下。FromBase64Reader
結構體實作了Read
特性,可以從經過Base64編碼的資料來源中,讀取出其解碼後的原始資料。
use std::io::Cursor;
use std::io::Read;
use base64_stream::FromBase64Reader;
let base64 = b"SGkgdGhlcmUsIHRoaXMgaXMgYSBzaW1wbGUgc2VudGVuY2UgdXNlZCBmb3IgdGVzdGluZyB0aGlzIGNyYXRlLiBJIGhvcGUgYWxsIGNhc2VzIGFyZSBjb3JyZWN0Lg==".to_vec();
let mut reader = FromBase64Reader::new(Cursor::new(base64));
let mut test_data = String::new();
reader.read_to_string(&mut test_data).unwrap();
assert_eq!("Hi there, this is a simple sentence used for testing this crate. I hope all cases are correct.", test_data);
FromBase64Writer
使用use
關鍵字來將base64_stream
這個crate底下的FromBase64Writer
結構體給引用到當前的程式範圍下。FromBase64Writer
結構體實作了Write
特性,可以輸入經過Base64編碼的資料,並輸出其解碼後的原始資料。
use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
use base64_stream::FromBase64Writer;
const DATA_FOLDER: &str = "data";
const DECODE_OUTPUT: &str = "decode_output.txt";
let base64 = b"SGkgdGhlcmUsIHRoaXMgaXMgYSBzaW1wbGUgc2VudGVuY2UgdXNlZCBmb3IgdGVzdGluZyB0aGlzIGNyYXRlLiBJIGhvcGUgYWxsIGNhc2VzIGFyZSBjb3JyZWN0Lg==".as_ref();
let file_path = Path::new("tests").join(DATA_FOLDER).join(DECODE_OUTPUT);
let test_data = File::create(file_path.as_path()).unwrap();
let mut writer = FromBase64Writer::new(test_data);
writer.write_all(base64).unwrap();
writer.flush().unwrap(); // the flush method is only used when the full base64 data has been written
assert_eq!("Hi there, this is a simple sentence used for testing this crate. I hope all cases are correct.", fs::read_to_string(file_path).unwrap());
改變緩衝空間的大小
預設的緩衝空間大小是4096個位元組。若您想要改變緩衝空間大小,可以使用new2
關聯函數並且明確地定義要使用的長度(透過泛型),來產生出上面介紹的結構體的實體。
例如,要將緩衝空間大小改為256個位元組的話,程式可以這樣寫:
use std::io::{Cursor, Read};
use base64_stream::ToBase64Reader;
use base64_stream::generic_array::typenum::U256;
let test_data = b"Hi there, this is a simple sentence used for testing this crate. I hope all cases are correct.".to_vec();
let mut reader: ToBase64Reader<_, U256> = ToBase64Reader::new2(Cursor::new(test_data));
let mut base64 = String::new();
reader.read_to_string(&mut base64).unwrap();
assert_eq!("SGkgdGhlcmUsIHRoaXMgaXMgYSBzaW1wbGUgc2VudGVuY2UgdXNlZCBmb3IgdGVzdGluZyB0aGlzIGNyYXRlLiBJIGhvcGUgYWxsIGNhc2VzIGFyZSBjb3JyZWN0Lg==", base64);