C# 玩转MongoDB(三)

  • A+
所属分类:.NET技术
摘要

前面两篇文章,已经讲解了C#对MongoDB的基本操作以及小文件的读写存储,那么对于大型(>=16M )文件呢?具体又该如何操作呢,本文主要以一个简单的小例子,简述C#如何通过GridFS进行MongoDB的大文件的操作,仅供学习分享使用,如有不足之处,还请指正。

前面两篇文章,已经讲解了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存储原理

一个文件存储在两个集合中,一个用于存储元数据(文件名称,类型,大小等内容,可便于索引),一个用于存储真实二进制数据(分块存储),如下所示:

C# 玩转MongoDB(三)

 

 GridFS安装

如果需要存储大型文件,则需要安装GridFS插件,如下所示:

项目--右键--管理Nuget程序包--打卡Nuget包管理器--浏览搜索MongoDB.Driver.GridFS--安装。如下所示:

C# 玩转MongoDB(三)

示例截图

首先是文件的查询,如下所示:

C# 玩转MongoDB(三)

文件的新增

C# 玩转MongoDB(三)

 

 核心代码

本示例主要是在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】,用于存储文件,如下所示:

C# 玩转MongoDB(三)

 

 通过查询fs.files集合,可以查找上传文件的列表,如下所示:

C# 玩转MongoDB(三)

 

 通过查询fs.chunks集合,可以查询文件的内容(二进制数据),如下所示:

C# 玩转MongoDB(三)

 

 注意:如果文件太大,在fs.chunks集合中,进行分片存储,n表示存储的顺序。

 以上就是C#操作MongoDB大文件存储的相关内容,旨在抛砖引玉,共同进步。

备注

点绛唇·感兴

【朝代】宋代 【作者】王禹偁【chēng】

雨恨云愁,江南依旧称佳丽。水村渔市,一缕孤烟细。
天际征鸿,遥认行如缀。平生事,此时凝睇,谁会凭栏意。(栏 通:阑)
C# 玩转MongoDB(三)