一文说通Dotnet操作MongoDB GridFS

补个技术债。

这个主题一直在列表中,今天把它补上。还有一个原因,就是网上能查到的代码,大多已经过期了。今天写的,是按最新的SDK做的例子。

一、MongoDB GridFS

先说说 GridFS。

MongoDB 是用 Bson 来存储数据的,每一行数据,称为 Document。每个 Document,大小有个上限,是16M,也就是说,结构化数据量大的空间占用是16M。注意,这个16M不是简单的内容总和,因为 Bson 对于字段名和类型有一定的特殊处理,实际存储的内容在计算上或多或少会有些变化,真正限制的是存储 Bson 的16M。

对于超过16M的数据,比方一个图片文件,MongoDB 也提供了一种存储方式,就是 GridFS。

不同于常规的结构数据存储,MongoDB 在存储 GridFS 数据时,用了两个集合(Collection):fs.files 和 fs.chunks。files 集合用来存储文件的相关信息,chunks 集合用来存储真正的文件块。

在SDK早期,这两个集合可以独立操作(因为这两个文件本身也是可操作的集合)。但实际应用中,这带来了相当程度的混乱。因此,在SDK 2.0以后,加入了一个桶(Bucket)的概念。从此,操作GridFS的流程变成了:开发者对「桶」进行操作,而「桶」在系统内部进行对两个集合的操作。

这就是上面说的过期代码的问题。

桶(Bucket)在MongoDB 中是个概念,对应着两个集合 files 和 chunks。桶的默认名称是 fs,对应的两个集合是 fs.files 和 fs.chunks。我们也可以给桶命名,例如 Test,则对应的集合会是 Test.files 和 Test.chunks。

二、操作GridFS

在操作GridFS时,我们会直接对桶(Bucket)操作。桶是建立在数据库 Database 上的。

1. 前置操作

要使用GridFS,我们需要引入一个库:

% dotnet add package MongoDB.Driver.GridFS

这个库是MongoDB官方的Dotnet库。

引入这个库时,系统会加入以下四个库:

  • MongoDB.Bson

  • MongoDB.Driver

  • MongoDB.Driver.Core

  • MongoDB.Driver.GridFS

熟悉MongoDB开发的会清楚,前三个是结构化操作需要引入的库。而第四个,就是 GridFS 操作的库。

2. 打开Bucket

其实我更愿意叫连接。就是连接到一个桶的意思。

var client = new MongoClient(connection_string);;
var database = client.GetDatabase("TestDatabase");
var bucket = new GridFSBucket(database);

三行代码,就连接到了一个Bucket。

如果需要连接到一个指定名称的Bucket,可以用下面的代码:

var bucket = new GridFSBucket(database, new GridFSBucketOptions
{BucketName = "Test",
});

3. 上传文件

上传文件,Bucket提供了两个方法

  • UploadFromBytes

  • UploadFromStream

以及对应的异步方法:

  • UploadFromBytesAsync

  • UploadFromStreamAsync

此外,还提供了一组流式操作方法:

  • OpenUploadStream

  • OpenUploadStreamAsync

这其实就是一个简单的IO操作:

using (var fs = new FileStream(file_path, FileMode.Open))
{var id = await bucket.UploadFromStreamAsync(file_name, fs);
}

成功后会返回ObjectId。

方法里的file_name,对应保存到files里的文件名filename。其实它是一个标识,用来让你查找文件用的。这个标识对应一个索引,是 { "filename" : 1, "uploadDate" : 1 }。对于相同的文件名,MongoDB视为同一个文件的多个版本,并通过uploadDate来区分版本。

我们来看一下保存后的数据:

Test.files

{ "_id" : ObjectId("60583228d37a5aec3c011557"), "length" : 73268, "chunkSize" : 261120, "uploadDate" : ISODate("2021-03-22T13:59:05.278+0800"), "md5" : "f2fe3c4e2828082ad9e82a11fabe6dd0", "filename" : "test.jpg"
}

Test.chunks

{ "_id" : ObjectId("60583229d37a5aec3c011558"), "files_id" : ObjectId("60583228d37a5aec3c011557"), "n" : 0, "data" : BinData(0, "/9j/...")
}

这是对应的两个集合里的一条数据。上面说的返回的ObjectId,是files里的_id值。

实际应用时,files集合里记录的文件信息有点少,我们需要加一些我们自己想存入的信息。这时候,可以这么写:

var options = new GridFSUploadOptions
{Metadata = new BsonDocument{{ "width", "1024" },{ "height", "768" }}
};
var id = await bucket.UploadFromStreamAsync(file_name, fs, options);

我们通过Metadata,把我们自己的数据也存到了files表里。

再看一下数据:

{ "_id" : ObjectId("60583228d37a5aec3c011557"), "length" : 73268, "chunkSize" : 261120, "uploadDate" : ISODate("2021-03-22T13:59:05.278+0800"), "md5" : "f2fe3c4e2828082ad9e82a11fabe6dd0", "filename" : "test.jpg", "metadata" : {"width" : "1024", "height" : "768"}
}

4. 下载文件

同上传类似,一组直接方法:

  • DownloadAsBytes

  • DownloadAsBytesAsync

  • DownloadToStream

  • DownloadToStreamAsync

  • DownloadAsBytesByName

  • DownloadAsBytesByNameAsync

  • DownloadToStreamByName

  • DownloadToStreamByNameAsync

以及一组流式方法:

  • OpenDownloadStream

  • OpenDownloadStreamAsync

  • OpenDownloadStreamByName

  • OpenDownloadStreamByNameAsync

内容上跟上传相似,多了一类用名称查找的方法。

看个简单的例子:

using (var fs = new FileStream(save_file_name, FileMode.Create))
{await bucket.DownloadToStreamAsync(new ObjectId("60583228d37a5aec3c011557")), fs);
}

给出ID,就是上面保存时返回的ID值,就可以下载文件到本地。

如果需要根据文件名下载,是这样的:

using (var fs = new FileStream(save_file_name, FileMode.Create))
{await bucket.DownloadToStreamByNameAsync("test.jpg", fs);
}

这样,我们就下载到文件的最新版本了。如果想获取文件的其它版本,可以加一个参数:

using (var fs = new FileStream(save_file_name, FileMode.Create))
{await bucket.DownloadToStreamByNameAsync("test.jpg", fs, new GridFSDownloadByNameOptions{Revision = 0});
}

5. 查找文件

查找文件跟结构化数据的查询没有区别,唯一的是引用的定义不同。

var filter = Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, "test.jpg");s
var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime);
var options = new GridFSFindOptions
{Limit = 1,Sort = sort
};
using (var cursor = bucket.Find(filter, options))
{var fileInfo = cursor.ToList().FirstOrDefault();
}

这个不详细说了,一看就明白。

6. 删除文件

也很简单,根据ID直接删。

删除一个文件:

bucket.Delete(new ObjectId("60583228d37a5aec3c011557"));

await bucket.DeleteAsync(new ObjectId("60583228d37a5aec3c011557"));

7. 删除Bucket

这也是一个方法:

bucket.Drop();

await bucket.DropAsync();

这是一次性删除存在Bucket中的所有数据的最快方法。

三、分片

要想用好MongoDB集群,就得玩好分片。放到MongoDB集群里的GridFS,也需要分片。

不过,GridFS分片很简单。

GridFS有两个集合,files 和 chunks。两个集合数据大小完全不一样。

files 集合只包含元数据,数据占用空间不大。如果没有特殊原因,可以不分片。如果一定要分片,用_id做片键就好,或者用自己存储的信息字段,也可以。

chunks 包含文件块,数据占用空间很大,需要分片。分片时,可以用 { files_id : 1, n : 1} 做片键,也可以就直接用 { files_id : 1 } 做片键。对于MongoDB 4.0及以上的版本,还可以用 { files_id : "hashed", n : 1 } 来做片键。

这就是今天全部的内容了。

多说两句:我发现很多人对MongoDB有一种莫名的抗拒,只是因为MongoDB不提供大家熟悉的SQL。其实,SQL也是一种应用语言。MongoDB虽然不使用SQL,但他的写法,也是一种很简单的语言结构,不用特别学习的。而且,MongoDB给我的最大惊喜是他的安装部署。MongoDB做成了一个绿色软件。一个服务器就一个程序,程序运行,数据库就起来了。做个集群,也只是一些简单的配置。加个帐号密码,就相当的安全了。这是多么爽的事啊?

喜欢就来个三连,让更多人因你而受益

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/303785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux path减少,Linux的环境变量PATH中所带来的问题及解决方法

Linux的环境变量PATH中所带来的问题及解决方法发布时间:2006-01-07 10:08:27来源:红联作者:yo本文主要讲述“.”在LINUX的环境变量PATH中所带来的问题&#xff0c;及解决的几种方法。正如很多人所知道的$PATH环境变量里存着一张目录列表&#xff0c;当用户要执行某一程序时&…

用VirtualWifi软件实现无线网卡同时连接多个AP。

2019独角兽企业重金招聘Python工程师标准>>> 用VirtualWifi软件实现无线网卡同时连接多个AP。 随着信息技术的发展&#xff0c;越来越多的人开始使用便捷的无线路由器&#xff08;AP&#xff09;组建家庭局域网。当你发现你周围有多个AP可以连接的时候&#xff0c;是…

职场感悟

1&#xff0c;尽量避免到有香港人和新加坡人做上司的外企&#xff0c;不是偏见&#xff0c;是很多事实的经验&#xff0c;不信你可以自己去尝试下&#xff0c;不过不要怪我没提醒过你。 2&#xff0c;不要去有第三方人事代理的外企&#xff0c;比如某人事会告诉你&#xff0c;你…

C语言 time函数 开销,C语言: 函数调用的开销

初学C语言的时候&#xff0c;我们有时会听说函数调用会有一定的开销&#xff0c;在进行了进一步学习之后&#xff0c;我们来看看原来听说的开销指的什么。下面是两个非常简单的样例&#xff0c;就不作解释了&#xff1a;函数调用版本C程序&#xff1a;#include int sum(int a, …

机器学习萌新必学的Top10算法

导读&#xff1a;在机器学习领域里&#xff0c;不存在一种万能的算法可以完美解决所有问题&#xff0c;尤其是像预测建模的监督学习里。所以&#xff0c;针对你要解决的问题&#xff0c;最好是尝试多种不同的算法。并借一个测试集来评估不同算法之间的表现&#xff0c;最后选出…

NET问答: C# 中有哪些 HttpPost 工具包

咨询区 Hooch&#xff1a;我会用 GET Request&#xff0c;但如何使用 Post Request 还得请教大家。回答区 Evan Mulawski&#xff1a;有多种方式可以使用 Http 的 GET 和 Post 请求。A方法&#xff1a;HttpClient (推荐)HttpClient 可用于 .NET Framework 4.5, .NET Standard 1…

wordpress安装 相关

64 位ubuntu 安装 32位兼容包apt-get install ia32-libs刚看了一下&#xff0c;wordpress才11个表太神奇了&#xff0c;这么复杂的系统&#xff0c;amazing参考以下文章http://9hills.us/2011/01/wordpress/http://dueam.org/2010/09/ubuntu-server-%E5%AE%89%E8%A3%85%E4%B8%8…

分支结构程序案例c语言,C语言学习之三——分支结构程序

运算符优先级&#xff1a;如下所示&#xff1a;image.png关系运算符和表达式关系运算符定义&#xff1a;在程序中经常需要比较两个量的大小关系,以决定程序下一步的工作。因此使用关系运算符进行比较(注&#xff1a;比较后的返回值为boolean值&#xff0c;即0或1(真为1&#xf…

老刘在微软Ignite China大会上聊低代码

观点概要1.低代码平台不是新概念&#xff0c;这种概念的上一个类似的提法是“所见即所得”。目前的低代码平台是依靠技术手段&#xff0c;不仅让所见即所得&#xff0c;还可以让一部分“所不见也所得”&#xff0c;比如背后的数据结构&#xff0c;业务逻辑&#xff0c;权限认证…

诺基亚首款Windows Phone智能手机将在年内推出

诺基亚高级官员星期日称&#xff0c;第一款采用微软Windows Phone操作系统的诺基亚智能手机的目标发布日期将在几天之内确定下来。诺基亚的目标是在将这种手机推向市场。诺基亚负责智能设备的执行副总裁乔哈洛&#xff08;Jo Harlow&#xff09;称&#xff0c;诺基亚和微软的技…

c语言四个数找大wxyz,2015年计算机二级《C语言》考试上机测试题(7)

31.有以下程序&#xff1a;#includemain(  ){char a[20]&#xff0c;b[20]&#xff0c;c[20];scanf("%s%s"&#xff0c;a&#xff0c;b);gets(c);printf("%S%S%s\n"&#xff0c;a&#xff0c;b&#xff0c;c);}程序运行时从第一列开始输入&#xff1a;Th…

IOT必备之MQTT结构分析,不进来看看?【后附源码】

全网唯一物联网MQTT协议报文结构分析以及基于C#代码的报文组装实现介绍MQTT是一种基于TCP/IP协议的应用层协议&#xff0c;它规定了不同应用之间进行数据交换时的传送格式。既然是协议&#xff0c;理论上可以被任何开发语言实现它&#xff0c;以运行在任何平台&#xff0c;这个…

要成为年薪五十万的数据分析师,除了技术还需要什么?

超过59%企业&#xff0c;将提高数据分析岗位数量----一流的数据分析师&#xff0c;年薪轻松突破50万想必同学们看到这个数据并不惊讶&#xff0c;如今在中国&#xff0c;各行各业对数据分析岗位的需求日益提高&#xff1a;在线社交媒体&#xff0c;希望通过数据时刻洞察用户关注…

ISAPI_Rewrite伪静态配置

第一:首先我们需要下载一个ISAPI_Rewrite,有精简版和完全版,一般精简版只能对服务器全局进行配置,而完整版可以对服务器上的各个网站进行伪静态配置.对于个人站长来说,精简版就足够了. 下载&#xff1a;http://www.isapirewrite.com/download/isapi_rwl_0055.msi 第二:下载完…

c语言电脑蓝屏代码,电脑蓝屏代码0x0000001a的解决方法

遇到蓝屏&#xff0c;第一时间记录停机码&#xff0c;如有导致蓝屏的文件名&#xff0c;那么排查的范围就会缩小。下面小编就为大家介绍电脑蓝屏代码0x0000001a的解决方法介绍&#xff0c;希望能对大家有所帮助&#xff01;方法/步骤1、先看这张截图的停机码所在位置。STOP后面…

关于序列建模,是时候抛弃RNN和LSTM了

作者表示&#xff1a;我们已经陷入 RNN、LSTM 和它们变体的坑中很多年&#xff0c;是时候抛弃它们了&#xff01;在 2014 年&#xff0c;RNN 和 LSTM 起死回生。我们都读过 Colah 的博客《Understanding LSTM Networks》和 Karpathy 的对 RNN 的颂歌《The Unreasonable Effecti…

如何运用并行编程Parallel提升任务执行效率

本文来自小易&#xff0c;【DoTNET技术圈】公众号已获得转载授权。《.NET并发变成实战》读后感&#xff1a;并行编程Parallel手打目录&#xff1a;一、前言二、任务并行库&#xff08;TPL&#xff09;的介绍三、Parallel.Invoke的使用四、Parallel.For的使用五、Parallel.ForEa…

VMware View 与Citrix XenDesktop对决之用户体验篇

看看真实的VMware View 与Citrix XenDesktop对比。 用户体验作为虚拟桌面成功的关键因素&#xff0c;看看VMware View如何在这方面立于不败之地。 对于测试中暴露的差异&#xff0c;您也许会觉得困惑。其实当您了解完下面这些&#xff0c;就全明白了。 Citrix的XenDesktop使用的…

c语言整数四则运算表达式的输出格式控制,Educoder CC++基本输入输出

第1关&#xff1a;重要的事情说三遍1.这里结合的一个具体场景是&#xff1a;程序接受一个输入字符&#xff0c;然后将该字符输出三遍&#xff0c;再输出一个&#xff01;。// 包含标准输入输出函数库#include // 定义main函数int main(){// 请在此添加‘重要的事情说三遍’的代…

通过 GitHub Actions 自动创建 Github Release

通过 GitHub Actions 自动创建 Github ReleaseIntro在 GitHub 上维护了几个小的开源项目&#xff0c;每次在发布新版本的时候会创建一个 release&#xff0c;这样可以比较方便的找到对应的版本的代码&#xff0c;不需要再人肉的从 git log 中找到指定的 commit&#xff0c;而且…