补个技术债。

这个主题一直在列表中,今天把它补上。还有一个原因,就是网上能查到的代码,大多已经过期了。今天写的,是按最新的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做成了一个绿色软件。一个服务器就一个程序,程序运行,数据库就起来了。做个集群,也只是一些简单的配置。加个帐号密码,就相当的安全了。这是多么爽的事啊?

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

一文说通Dotnet操作MongoDB GridFS相关推荐

  1. 一文说通Dotnet Core的后台任务

    这是一文说通系列的第二篇,里面有些内容会用到第一篇中间件的部分概念.如果需要,可以参看第一篇:一文说通Dotnet Core的中间件   一.前言 后台任务在一些特殊的应用场合,有相当的需求. 比方, ...

  2. PHP操作MongoDB GridFS 存储文件

    PHP操作MongoDB GridFS 存储文件,如图片文件 我的测试代码: 1.前端上传文件html index.html <!DOCTYPE html PUBLIC "-//W3C ...

  3. 一文说通Dotnet的委托

    简单的概念,也需要经常看看.   一.前言 先简单说说Delegate的由来.最早在C/C++中,有一个概念叫函数指针.其实就是一个内存指针,指向一个函数.调用函数时,只要调用函数指针就可以了,至于函 ...

  4. mongodb gridfs php,PHP操作MongoDB GridFS 存储文件的详解

    //初始化gridfs $conn = new Mongo(); //连接MongoDB $db = $conn->photos; //选择数据库 $grid = $db->getGrid ...

  5. Python+Streamlit aggrid+MongoDB GridFS构建低代码文档管理应用(文档查询下载实用篇)

    1. Sreamlit aggrid简介 Sreamlit aggrid是Streamlit的Ag-Grid组件的实现,在Python Streamlit框架下,更加灵活的使用表格,包括分组.排序.编 ...

  6. MongoDB文档查询操作(三)

    关于MongoDB中的查询,我们已经连着介绍了两篇文章了,本文我们来介绍另外一个查询概念游标. 本文是MongoDB系列的第七篇文章,了解前面的文章有助于更好的理解本文: 1.Linux上安装Mong ...

  7. MongoDB文档查询操作(一)

    上篇文章我们主要介绍了MongoDB的修改操作,本文我们来看看查询操作. 本文是MongoDB系列的第五篇文章,了解前面的文章有助于更好的理解本文: 1.Linux上安装MongoDB 2.Mongo ...

  8. MongoDB(5)文档 CRUD 操作

    MongoDB 入门专栏 http://blog.csdn.net/column/details/19681.html MongoDB 文档 CRUD 操作 查询文档 基本使用 mongodb 查询文 ...

  9. 分布式文档存储独角兽MongoDB——系统结构(1)

    分布式文档存储独角兽MongoDB 一.MongoDB系统结构 1.1 NoSQL 和 MongoDB NoSQL=Not Only SQL,支持类似SQL的功能, 与Relational Datab ...

最新文章

  1. 代理(Proxy)及常见示例
  2. 重试次数配置_TestNG实践——2.用例失败重试
  3. [Pytorch].pth转.pt文件
  4. IntelliJ IDEA常用的快捷键(代码提示/注释代码/加入类注释和方法注释Javadoc)
  5. vmware的3种网络模式
  6. 多代理集群调度:可伸缩性和灵活性
  7. SQL SERVER 2005 T_SQL新的特性以及解决并发
  8. 中蜂几月份自然分蜂_蜜蜂什么时候自然分蜂?
  9. (良心)世上最全设计模式导读(含难度预警与使用频率完整版)
  10. 图像 super-resolution restruction 的各种主流实现方式
  11. 谍影重重,由片名想起
  12. MySQL基础教程5-数据库基础回顾
  13. PHP 静态缓存 OB系列函数
  14. 碧蓝航线频道和服务器所在的文件夹,碧蓝航线如何反和谐 反和谐方法
  15. Android 启动过程介绍
  16. idea 下载vue 插件
  17. “阿里云大数据技术实战训练营”江苏省大学生万人计划学术冬令营活动成功举行...
  18. matlab复数曲线拟合,lsqcurvefit拟合结果为复数
  19. docker创建busybox
  20. 滕州一中计算机竞赛,滕州一中3名学生“叶圣陶杯”获佳绩!高三学生蒋雨含获决赛一等奖...

热门文章

  1. python 多个列表_Python同时迭代多个列表
  2. apache的rewrite模块实例操作
  3. 语法上的小trick
  4. 探究Java如何实现原子操作(atomic operation)
  5. VMware——安装CentOS
  6. Couchbase概述
  7. Oracle中的USEREVN()
  8. 使用 Apache Pig 处理数据5
  9. net读取exchange数据
  10. [导入]体验Asp.Net Mvc Preview5(3)-探索ModelBinder的工作原理