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」是筆者開發的套件,提供了ToBase64ReaderToBase64WriterFromBase64ReaderFromBase64Writer這四樣工具,分別實作了ReadWrite特性,可以進行全方位的Base64編解碼。

Crates.io

Cargo.toml

base64-stream = "*"

使用方法

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);