MongoDB是文件導向的資料庫,適合用來儲存無需複雜查詢的大量資料,也可以用來儲存檔案。未超過16793600位元組(約16MB)的BSON文件能直接存在一般的集合(collection)之中,如果將檔案放進BSON文件中且沒有超過大小限制的話,則檔案可以直接存進一般的集合;如果文件會超過16793600位元組,也可以使用MongoDB提供的GridFS功能,自動將檔案切割成多個組塊(chunk)來儲存,一個組塊預設的大小為255KiB。然而,MongoDB本身並沒有判斷存入資料庫的檔案是否有重複的功能,也無法根據要存入的檔案大小來自動選擇是要存到集合裡,還是要存到GridFS中。



為了一勞永逸地解決這個問題,就必須要設計一套框架,來統一管理MongoDB內的檔案。

Mongo File Center (File Center on MongoDB)

「Mongo File Center」是筆者開發的套件,旨在建立一個易於使用且不會有冗餘的MongoDB檔案儲存空間。

Crates.io

https://crates.io/crates/mongo-file-center

Cargo.toml

mongo-file-center = "*"

使用方法

一個MongoDB的資料庫可以擁有一個檔案中心。一開始要先以MongoDB的主機名稱、連接埠和資料庫名稱,呼叫FileCenter結構體的「new」關聯函數,來產生出新的FileCenter結構實體。此時順利的話,檔案中心就建立出來了。

要存放檔案進檔案中心中,可以使用FileCenter結構實體以put_file_by_為前綴名稱的方法,這類方法可以將檔案或是記憶體中的資料放進檔案中心,其中,以_temporarily作為名稱後綴的方法只會將檔案保存最多一分鐘,在這期間若這檔案有被取用,就會在取用之後被刪除;而不以_temporarily作為名稱後綴的方法,檔案在檔案中心內會永久被保存下來,同樣的檔案內容在同一資料庫中不會重複被儲存(會用SHA3-256來計算checksum,判斷檔案內容是否相同)。不管是暫時性的還是永久性的,put_file_by_為前綴名稱的方法在檔案儲存成功時,都會回傳FileItem結構實體,用來表示位於檔案中心內的檔案。

FileItem結構實體的get_object_id方法可以取得檔案在檔案中心的Object ID。FileCenter結構實體的get_file_item_by_id方法可以傳入一個檔案在檔案中心的Object ID,來取得FileItem結構實體。FileCenter結構實體的delete_file_item_by_id方法可以傳入一個檔案在檔案中心的Object ID,來將其刪除。此外,FileCenter結構實體還可以使用encrypt_iddecrypt_id_token方法來加密或是解密Object ID,加密後Object ID才可以使其安全地被分享出去。

FileItem結構實體的into_file_data方法可以用來取得檔案的內容,文章前面有提到,MongoDB的檔案可以存在集合或是GridFS中。因此into_file_data方法所回傳的FileData結構實體就會有FileData::GridFSFileData::FileData這兩種變體。

以下是一個標準的使用範例:

extern crate mongo_file_center;
extern crate mime;

use mongo_file_center::{FileCenter, FileData};

const HOST: &str = "localhost";
const PORT: u16 = 27017;

let database = "test_my_file_storage";

let file_center = FileCenter::new(HOST, PORT, database).unwrap();

let file = file_center.put_file_by_path("/path/to/file", Some("file_name"), Some(mime::IMAGE_JPEG)).unwrap();

let file_id = file.get_object_id();

let id_token = file_center.encrypt_id(&file_id); // this token is safe in public

let file_id = file_center.decrypt_id_token(&id_token).unwrap();

let r_file = file_center.get_file_item_by_id(file_id).unwrap().unwrap();

match r_file.into_file_data() {
    FileData::GridFS(file) => {
        // do something
    }
    FileData::FileData(data) => {
        // do something
    }
}

預設情況下,檔案若大於等於255KiB,就會使用GridFS來儲存,否則的話就會直接存在集合內。如果要更改這項設定,可以在建立檔案中心後使用FileCenter結構實體的set_file_size_threshold方法來更改,或是在尚未建立檔案中心前,使用FileCenter結構體的new_with_file_size_threshold關聯函數來建立檔案中心。

如果要刪除整個檔案中心,可以使用FileCenter結構實體drop_file_center方法。