- A+
前面两篇文章,已经讲解了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】
天际征鸿,遥认行如缀。平生事,此时凝睇,谁会凭栏意。(栏 通:阑)