MongoDb GridFS 是MongoDB的文件存儲方案,主要用於存儲和恢復那些超過16M(BSON文件限制)的文件(如:圖片、音頻等),對大文件有着更好的性能。
要在C#中使用GridFS,首先安裝Nuget包: MongoDB.Driver.GridFS
Bucket
GridFS中的數據也是分集合存儲的,每個集合叫一個bucket,每個bucket里面可以存儲多個文件:
在C#中使用bucket和使用集合差不多,創建一個GridFSBucket對象即可。
var bucket = new GridFSBucket(db, new GridFSBucketOptions()
{
BucketName = "my_bucket",
ChunkSizeBytes = 256 * 1024, //塊大小
});
對於bucket,主要的參數就是bucket名稱和塊大小,它也不用手動創建,在使用的時候會自動創建。
上傳數據:
對於byte[]數組,可以直接使用UploadFromBytes上傳:
var data = Enumerable.Range(0, 256).Select(i => (byte)i).ToArray();
var id = bucket.UploadFromBytes("filename", data);
主要參數是傳入一個文件名(文件名並不要求唯一,同一bucket類可以重復),返回數據id。
也可以帶入metadata信息。
var options = new GridFSUploadOptions
{
Metadata = new BsonDocument
{
{"resolution", "1080P"},
{"copyrighted", true}
},
DisableMD5 = true,
};
var id = bucket.UploadFromBytes("filename", data, options);
上傳Stream:
使用UploadFromBytes上傳的時候,需要將上傳的數據全部讀取到內存,如果需要上傳的是較大的文件,這種方式並不合適。此時可以使用UploadFromStream函數上傳。
using (var fs = File.OpenRead(@"r:\123.jpg"))
{
bucket.UploadFromStream("123.jpg", fs);
}
也可以使用OpenUploadStream的方式打開為一個Stream,然后通過推的方式完成上傳。
using (var fs = File.OpenRead(@"r:\123.jpg"))
using (var upload = bucket.OpenUploadStream("123.jpg"))
{
fs.Copy(upload);
upload.Close();
Console.WriteLine(upload.Id);
}
注意:這個stream必須手動Close,只是調用Dispose不會寫入到bucket中去。不知道算不算MongoDB API的一個bug。另外,GridFS的這個GridFSUploadStream.Close是通過隱藏的方式實現的,如果將他轉換成了Stream使用,此時調用Close不能上傳至GridFS,需要將其轉換成GridFSUploadStream才行。
下載:
下載的API和上傳類似,如下是幾個基本示例:
var id = new ObjectId("5b6ba04c77850928a438b1b2");
var bytes = bucket.DownloadAsBytes(id);
using (var target = File.Create(@"r:\target.jpg"))
{
bucket.DownloadToStream(id, target);
}
using (var download = bucket.OpenDownloadStream(id))
using (var target = File.Create(@"r:\target.jpg"))
{
download.CopyTo(target);
}
查詢:
GridFS 用兩個集合來存儲一個文件:fs.files與fs.chunks。對於bucket中的記錄,實際上存成了兩個部分。
-
文件的實際內容被存在chunks(二進制數據)中,拆分成了一堆chunk存儲
-
相關的描述信息files集合中,它是一個標准的mongodb的文檔模型,其結構為:
{ "_id" : <ObjectId>, "length" : <num>, "chunkSize" : <num>, "uploadDate" : <timestamp>, "md5" : <hash>, "filename" : <string>, "contentType" : <string>, "aliases" : <string array>, "metadata" : <any>, }
gridfs對filename 和uploadDate字段做了索引的,通過它們查詢能獲取到較好的性能。
參考文檔: https://docs.mongodb.com/manual/core/gridfs/
gridfs的api中封裝了對它的查詢,也是遵循標准的查詢API的
var filter = new {filename = "123.jpg"};
var result = bucket.Find(filter.ToBsonDocument()).ToList();
另外,也可以直接操作fs.files集合。
var files = db.GetCollection<BsonDocument>("my_bucket.files");
更新:
GridFS的API支持對文檔重命名:
bucket.Rename(id, "NewName");
但沒有找打更新MetaData的API,在網上找了一下,有人已經提了這個問題。結論是目前官方沒有封裝,不過可以從fs.files集合中直接更新metadata。示例如下。
var files = db.GetCollection<BsonDocument>("my_bucket.files");
var filter = new BsonDocument()
{
["_id"] = id
};
var update = new BsonDocument()
{
["$set"] = new BsonDocument()
{
["metadata"] = metaData
}
};
files.UpdateOne(filter, update);
小結
Gridfs的API封裝還算好用的,就是不過不知道為什么沒有封裝對描述信息的更新操作。官方文檔鏈接如下,要深入學習的可以查看一下。
本文這里主要介紹的是GridFS的使用,但需要注意的是,GridFS本身並不是分布式存儲服務,它仍然依賴於MongoDB,並不是解決大規模的分布式存儲的問題的,需要大容量存儲和負載均衡等場景建議還是交給FastDFS等專業的服務來。
不過,對於一些性能和容量要求不高的企業應用,存儲一些圖片,附件等小規模存儲場景,還是非常合適的。部署和使用都是非常方便快捷的。
另外,網上也有一些深入點介紹GridFS的文檔,也可以看下。