前面兩篇文章,已經講解了C#對MongoDB的基本操作以及小文件的讀寫存儲,那么對於大型(>=16M)文件呢?具體又該如何操作呢,本文主要以一個簡單的小例子,簡述C#如何通過GridFS進行MongoDB的大文件的操作,僅供學習分享使用,如有不足之處,還請指正。
什么是GridFS?
在實現GridFS方式前我先講講它的原理,為什么可以存大文件。驅動首先會在當前數據庫創建兩個集合:"fs.files"和"fs.chunks"集合,前者記錄了文件名,文件創建時間,文件類型等基本信息;后者分塊存儲了文件的二進制數據(並支持加密這些二進制數據)。分塊的意思是把文件按照指定大小分割,然后存入多個文檔中。"fs.files"怎么知道它對應的文件二進制數據在哪些塊呢?那是因為在"fs.chunks"中有個"files_id"鍵,它對應"fs.files"的"_id"。"fs.chunks"還有一個鍵(int型)"n",它表明這些塊的先后順序。這兩個集合名中的"fs"也是可以通過參數自定義的。
GridFS存儲原理
一個文件存儲在兩個集合中,一個用於存儲元數據(文件名稱,類型,大小等內容,可便於索引),一個用於存儲真實二進制數據(分塊存儲),如下所示:
GridFS安裝
如果需要存儲大型文件,則需要安裝GridFS插件,如下所示:
項目--右鍵--管理Nuget程序包--打卡Nuget包管理器--瀏覽搜索MongoDB.Driver.GridFS--安裝。如下所示:
示例截圖
首先是文件的查詢,如下所示:
文件的新增
核心代碼
本示例主要是在MongoDB中進行文件的操作,所以之前的MongoHelper已不再適用,本例新增了文件專用幫助類MongoFileHelper,如下所示:
1 using MongoDB.Bson; 2 using MongoDB.Driver; 3 using MongoDB.Driver.GridFS; 4 using System; 5 using System.Collections.Generic; 6 using System.IO; 7 using System.Linq; 8 using System.Text; 9 using System.Threading.Tasks; 10 11 namespace DemoMongo.Common 12 { 13 14 public class MongoFileHelper 15 { 16 17 private string connStr = "mongodb://127.0.0.1:27017";//服務器網址 18 19 private string dbName = "hexdb";//數據庫名稱 20 21 private IMongoClient client;//連接客戶端 22 23 private IMongoDatabase db;//連接數據庫 24 25 private string collName;//集合名稱 26 27 public MongoFileHelper() 28 { 29 30 } 31 32 public MongoFileHelper(string connStr, string dbName, string collName) 33 { 34 this.connStr = connStr; 35 this.dbName = dbName; 36 this.collName = collName; 37 this.Init(); 38 } 39 40 /// <summary> 41 /// 初始化連接客戶端 42 /// </summary> 43 private void Init() 44 { 45 if (client == null) 46 { 47 client = new MongoClient(this.connStr); 48 } 49 if (db == null) 50 { 51 db = client.GetDatabase(this.dbName); 52 } 53 } 54 55 /// <summary> 56 /// 通過字節方式上傳 57 /// </summary> 58 /// <param name="filePath"></param> 59 public void UploadFile(string filePath) 60 { 61 IGridFSBucket bucket = new GridFSBucket(db); 62 byte[] source = File.ReadAllBytes(filePath); 63 string fileName = Path.GetFileName(filePath); 64 var options = new GridFSUploadOptions 65 { 66 ChunkSizeBytes = 64512, // 63KB 67 Metadata = new BsonDocument 68 { 69 { "resolution", "1080P" }, 70 { "copyrighted", true } 71 } 72 }; 73 var id = bucket.UploadFromBytes(fileName, source); 74 //返回的ID,表示文件的唯一ID 75 76 77 } 78 79 /// <summary> 80 /// 通過Stream方式上傳 81 /// </summary> 82 /// <param name="filePath"></param> 83 public void UploadFile2(string filePath) 84 { 85 IGridFSBucket bucket = new GridFSBucket(db); 86 var stream = new FileStream(filePath, FileMode.Open); 87 88 string fileName = Path.GetFileName(filePath); 89 var options = new GridFSUploadOptions 90 { 91 ChunkSizeBytes = 64512, // 63KB 92 Metadata = new BsonDocument 93 { 94 { "resolution", "1080P" }, 95 { "copyrighted", true } 96 } 97 }; 98 var id = bucket.UploadFromStream(fileName, stream); 99 //返回的ID,表示文件的唯一ID 100 101 102 } 103 104 /// <summary> 105 /// 通過字節寫入到流 106 /// </summary> 107 /// <param name="filePath"></param> 108 public void UploadFile3(string filePath) 109 { 110 IGridFSBucket bucket = new GridFSBucket(db); 111 byte[] source = File.ReadAllBytes(filePath); 112 string fileName = Path.GetFileName(filePath); 113 var options = new GridFSUploadOptions 114 { 115 ChunkSizeBytes = 64512, // 63KB 116 Metadata = new BsonDocument 117 { 118 { "resolution", "1080P" }, 119 { "copyrighted", true } 120 } 121 }; 122 using (var stream = bucket.OpenUploadStream(fileName, options)) 123 { 124 var id = stream.Id; 125 stream.Write(source, 0, source.Length); 126 stream.Close(); 127 } 128 } 129 130 /// <summary> 131 /// 下載文件 132 /// </summary> 133 /// <param name="id"></param> 134 public void DownloadFile(ObjectId id,string filePath) 135 { 136 IGridFSBucket bucket = new GridFSBucket(db); 137 byte[] source = bucket.DownloadAsBytes(id); 138 //返回的字節內容 139 //var bytes = await bucket.DownloadAsBytesAsync(id); 140 using (Stream stream = new FileStream(filePath, FileMode.OpenOrCreate)) { 141 stream.Write(source, 0, source.Length); 142 } 143 } 144 145 public void DownloadFile2(ObjectId id) 146 { 147 IGridFSBucket bucket = new GridFSBucket(db); 148 Stream destination = null; 149 bucket.DownloadToStream(id, destination); 150 //返回的字節內容 151 //await bucket.DownloadToStreamAsync(id, destination); 152 153 } 154 155 public void DownloadFile3(ObjectId id) 156 { 157 IGridFSBucket bucket = new GridFSBucket(db); 158 Stream destination = null; 159 using (var stream = bucket.OpenDownloadStream(id)) 160 { 161 // read from stream until end of file is reached 162 stream.Close(); 163 } 164 } 165 166 public void DownloadFile4(string fileName) 167 { 168 IGridFSBucket bucket = new GridFSBucket(db); 169 var bytes = bucket.DownloadAsBytesByName(fileName); 170 171 // or 172 173 Stream destination = null; 174 bucket.DownloadToStreamByName(fileName, destination); 175 176 // or 177 178 using (var stream = bucket.OpenDownloadStreamByName(fileName)) 179 { 180 // read from stream until end of file is reached 181 stream.Close(); 182 } 183 } 184 185 public List<MongoFile> FindFiles() 186 { 187 IGridFSBucket bucket = new GridFSBucket(db); 188 var filter = Builders<GridFSFileInfo>.Filter.And( 189 //Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, string.Empty), 190 Builders<GridFSFileInfo>.Filter.Gte(x => x.UploadDateTime, new DateTime(2015, 1, 1, 0, 0, 0, DateTimeKind.Utc)), 191 Builders<GridFSFileInfo>.Filter.Lt(x => x.UploadDateTime, new DateTime(2022, 2, 1, 0, 0, 0, DateTimeKind.Utc))); 192 var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime); 193 var options = new GridFSFindOptions 194 { 195 //Limit = 1, 196 Sort = sort 197 }; 198 List<MongoFile> lstFiles = new List<MongoFile>(); 199 using (var cursor = bucket.Find(filter, options)) 200 { 201 var fileInfos = cursor.ToList(); 202 foreach (var fileInfo in fileInfos) { 203 MongoFile f = new MongoFile() 204 { 205 Id=fileInfo.Id, 206 name = fileInfo.Filename, 207 suffix = Path.GetExtension(fileInfo.Filename), 208 size = int.Parse(fileInfo.Length.ToString()) 209 }; 210 lstFiles.Add(f); 211 } 212 } 213 return lstFiles; 214 } 215 216 public List<MongoFile> FindFileByName(string fileName) 217 { 218 IGridFSBucket bucket = new GridFSBucket(db); 219 var filter = Builders<GridFSFileInfo>.Filter.And( 220 Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, fileName), 221 Builders<GridFSFileInfo>.Filter.Gte(x => x.UploadDateTime, new DateTime(2015, 1, 1, 0, 0, 0, DateTimeKind.Utc)), 222 Builders<GridFSFileInfo>.Filter.Lt(x => x.UploadDateTime, new DateTime(2015, 2, 1, 0, 0, 0, DateTimeKind.Utc))); 223 var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime); 224 var options = new GridFSFindOptions 225 { 226 //Limit = 1, 227 Sort = sort 228 }; 229 List<MongoFile> lstFiles = new List<MongoFile>(); 230 using (var cursor = bucket.Find(filter, options)) 231 { 232 var fileInfos = cursor.ToList(); 233 foreach (var fileInfo in fileInfos) 234 { 235 MongoFile f = new MongoFile() 236 { 237 Id = fileInfo.Id, 238 name = fileInfo.Filename, 239 suffix = Path.GetExtension(fileInfo.Filename), 240 size = int.Parse(fileInfo.Length.ToString()) 241 }; 242 lstFiles.Add(f); 243 } 244 } 245 return lstFiles; 246 } 247 } 248 }
然后操作時,調用幫助類即可,如下所示:
查詢調用
1 private void btnQuery_Click(object sender, EventArgs e) 2 { 3 string name = this.txtName.Text.Trim(); 4 List<MongoFile> fileInfos = new List<MongoFile>(); 5 if (string.IsNullOrEmpty(name)) 6 { 7 fileInfos = helper.FindFiles(); 8 } 9 else { 10 fileInfos = helper.FindFileByName(name); 11 } 12 13 this.dgView.AutoGenerateColumns = false; 14 this.bsView.DataSource = fileInfos; 15 this.dgView.DataSource = this.bsView; 16 }
下載調用
1 private void dgView_CellContentClick(object sender, DataGridViewCellEventArgs e) 2 { 3 if (e.ColumnIndex == 3) { 4 //第3個是下載按鈕 5 SaveFileDialog sfd = new SaveFileDialog(); 6 7 var file = (MongoFile)(this.dgView.Rows[e.RowIndex].DataBoundItem); 8 sfd.FileName = file.name; 9 sfd.Title = "請保存文件"; 10 if (DialogResult.OK == sfd.ShowDialog()) 11 { 12 helper.DownloadFile(file.Id,sfd.FileName); 13 MessageBox.Show("保存成功"); 14 15 } 16 } 17 }
保存調用
1 private void btnSave_Click(object sender, EventArgs e) 2 { 3 string filePath = this.txtPath.Text; 4 if (!string.IsNullOrEmpty(filePath)) 5 { 6 this.helper.UploadFile(filePath); 7 MessageBox.Show("保存成功"); 8 } 9 else { 10 MessageBox.Show("請先選擇文件"); 11 } 12 13 }
MongoDB查詢
當通過GridFS方式保存文件成功后,會在GridFS Buckets下生成fs對象,且在集合下生成兩個集合【fs.files,fs.chunks】,用於存儲文件,如下所示:
通過查詢fs.files集合,可以查找上傳文件的列表,如下所示:
通過查詢fs.chunks集合,可以查詢文件的內容(二進制數據),如下所示:
注意:如果文件太大,在fs.chunks集合中,進行分片存儲,n表示存儲的順序。
以上就是C#操作MongoDB大文件存儲的相關內容,旨在拋磚引玉,共同進步。
備注
點絳唇·感興
【朝代】宋代 【作者】王禹偁【chēng】
天際征鴻,遙認行如綴。平生事,此時凝睇,誰會憑欄意。(欄 通:闌)
