存储大文件
概述
在本指南中,您将学习如何使用GridFS在MongoDB中存储和检索大文件。GridFS是由C++驱动程序实现的一个规范,它描述了在存储文件时如何将其分割成块,以及在检索文件时如何重新组装这些块。驱动程序对GridFS的实现是一个抽象层,它管理文件存储的操作和组织。
如果您文件的尺寸超过了BSON文档大小限制(16MB),请使用GridFS。有关GridFS是否适合您的用例的更详细信息,请参阅GridFS MongoDB服务器手册。
GridFS是如何工作的
GridFS将文件组织在一个名为“存储桶”的结构中,它是一组包含文件块及其描述信息的MongoDB集合。存储桶包含以下集合,这些集合的命名遵循GridFS规范中定义的约定
chunks
集合,用于存储二进制文件块files
集合,用于存储文件元数据
当您首次向其中写入数据时,如果GridFS存储桶不存在,则驱动程序会创建它。存储桶包含以默认存储桶名称fs
开头的先前集合,除非您指定了不同的名称。为了确保高效地检索文件和相关元数据,驱动程序会在每个集合上创建索引。驱动程序确保在执行GridFS存储桶的读写操作之前,这些索引存在。
有关GridFS索引的更多信息,请参阅MongoDB服务器手册中的GridFS Indexes。
当使用GridFS存储文件时,驱动程序会将文件分割成更小的块,每个块由chunks
集合中的一个单独文档表示。它还在files
集合中创建一个文档,其中包含文件ID、文件名和其他文件元数据。您可以通过传递一个流到C++驱动程序以消耗它,或者通过创建一个新的流并直接写入它来上传文件。
以下图显示了GridFS在上传到存储桶时如何分割文件

检索文件时,GridFS从指定存储桶中的files
集合中获取元数据,并使用这些信息从chunks
集合中的文档中重建文件。您可以通过将内容写入现有流或创建一个指向文件的新流来读取文件。
创建GridFS存储桶
要开始将文件存储或从GridFS检索文件,请调用数据库上的gridfs_bucket()
方法。此方法访问现有的存储桶或创建一个新存储桶(如果不存在)。
以下示例在db
数据库上调用gridfs_bucket()
方法
auto bucket = db.gridfs_bucket();
自定义存储桶
您可以通过将mongocxx::options::gridfs::bucket
类的实例作为可选参数传递给gridfs_bucket()
方法来自定义GridFS存储桶配置。以下表格描述了您可以在mongocxx::options::gridfs::bucket
实例中设置的字段
字段 | 描述 |
---|---|
| 指定用于文件和块集合的前缀的存储桶名称。默认值是 "fs" 。类型: std::string |
| 指定GridFS将文件拆分成的块大小。默认值是 261120 。类型: std::int32_t |
| 指定用于存储桶操作的读取关注度。默认值是数据库的读取关注度。 类型: mongocxx::read_concern |
| 指定用于存储桶操作的读取偏好。默认值是数据库的读取偏好。 类型: mongocxx::read_preference |
| 指定用于存储桶操作的写入关注度。默认值是数据库的写入关注度。 类型: mongocxx::write_concern |
以下示例通过设置mongocxx::options::gridfs::bucket
实例的bucket_name
字段创建一个名为"myCustomBucket"
的存储桶
mongocxx::options::gridfs::bucket opts; opts.bucket_name("myCustomBucket"); auto bucket = db.gridfs_bucket(opts);
上传文件
您可以使用以下方法将文件上传到GridFS存储桶
open_upload_stream(): 打开一个新上传流,您可以将文件内容写入其中
upload_from_stream(): 将现有流的内容上传到GridFS文件
写入上传流
使用 open_upload_stream()
方法创建指定文件名的上传流。该 open_upload_stream()
方法允许你在 options::gridfs::upload
实例中指定配置信息,并将其作为参数传递。
以下示例使用上传流执行以下操作
设置选项实例的
chunk_size_bytes
字段为名为
"my_file"
的新 GridFS 文件打开一个可写流并应用chunk_size_bytes
选项调用
write()
方法将数据写入由流指向的my_file
调用
close()
方法关闭指向my_file
的流
mongocxx::options::gridfs::upload opts; opts.chunk_size_bytes(1048576); auto uploader = bucket.open_upload_stream("my_file", opts); // ASCII for "HelloWorld" std::uint8_t bytes[10] = {72, 101, 108, 108, 111, 87, 111, 114, 108, 100}; for (auto i = 0; i < 5; ++i) { uploader.write(bytes, 10); } uploader.close();
上传现有流
使用 upload_from_stream()
方法将流的内容上传到新的 GridFS 文件。该 upload_from_stream()
方法允许你在 options::gridfs::upload
实例中指定配置信息,并将其作为参数传递。
以下示例执行以下操作
以二进制读取模式打开位于
/path/to/input_file
的文件作为流调用
upload_from_stream()
方法将流的内容上传到名为"new_file"
的 GridFS 文件
std::ifstream file("/path/to/input_file", std::ios::binary); bucket.upload_from_stream("new_file", &file);
检索文件信息
在本节中,您可以学习如何检索存储在 GridFS 桶的 files
集合中的文件元数据。元数据包含有关所引用文件的信息,包括
文件的
_id
文件名
文件长度/大小
上传日期和时间
一个可以存储其他信息的
metadata
文档
要从 GridFS 桶检索文件,请调用您的桶上的 mongocxx::gridfs::bucket::find()
方法。该方法返回一个 mongocxx::cursor
实例,您可以从中访问结果。有关游标的更多信息,请参阅从游标访问数据指南。
示例
以下代码示例演示了如何从GridFS存储桶中的文件检索并打印文件元数据。它使用一个for
循环遍历返回的游标并显示在上传文件示例中上传的文件内容。
auto cursor = bucket.find({}); for (auto&& doc : cursor) { std::cout << bsoncxx::to_json(doc) << std::endl; }
{ "_id" : { "$oid" : "..." }, "length" : 13, "chunkSize" : 261120, "uploadDate" : { "$date" : ... }, "filename" : "new_file" } { "_id" : { "$oid" : "..." }, "length" : 50, "chunkSize" : 1048576, "uploadDate" : { "$date" : ... }, "filename" : "my_file" }
find()
方法接受各种查询规范。您可以使用其mongocxx::options::find
参数来指定排序顺序、返回的最大文档数以及返回前要跳过的文档数。要查看可用选项的列表,请参阅API文档。
下载文件
您可以通过以下方法从GridFS存储桶下载文件
open_download_stream():打开一个新下载流,从中您可以读取文件内容
download_to_stream():将整个文件写入现有的下载流
从下载流中读取
您可以使用open_download_stream()
方法创建下载流来从MongoDB数据库下载文件。
此示例使用下载流执行以下操作
检索名为
"new_file"
的GridFS文件的_id
值将
_id
值传递给open_download_stream()
方法以将文件作为可读流打开创建一个
buffer
向量以存储文件内容调用
read()
方法将文件内容从downloader
流读取到向量中
auto doc = db["fs.files"].find_one(make_document(kvp("filename", "new_file"))); auto id = doc->view()["_id"].get_value(); auto downloader = bucket.open_download_stream(id); std::vector<uint8_t> buffer(downloader.file_length()); downloader.read(buffer.data(), buffer.size());
下载到现有流
您可以通过在您的bucket上调用download_to_stream()
方法将GridFS文件的内容下载到现有流中。
以下示例执行以下操作
以二进制写入模式打开位于
/path/to/output_file
的文件检索名为
"new_file"
的GridFS文件的_id
值将
_id
值传递给download_to_stream()
以将文件下载到流中
std::ofstream output_file("/path/to/output_file", std::ios::binary); auto doc = db["fs.files"].find_one(make_document(kvp("filename", "new_file"))); auto id = doc->view()["_id"].get_value(); bucket.download_to_stream(id, &output_file);
删除文件
使用delete_file()
方法删除文件的集合文档和与bucket关联的块。这实际上会删除文件。您必须通过其_id
字段指定文件,而不是其文件名。
以下示例展示了如何通过将文件的_id
值传递给delete_file()
来删除名为"my_file"
的文件。
auto doc = db["fs.files"].find_one(make_document(kvp("filename", "my_file"))); auto id = doc->view()["_id"].get_value(); bucket.delete_file(id);
注意
文件版本
delete_file()
方法一次只能删除一个文件。如果您想删除每个文件版本,或者具有相同文件名但上传时间不同的文件,请收集每个版本的_id
值。然后,在每个单独的调用中将每个_id
值传递给delete_file()
方法。
API 文档
要了解如何使用 C++ 驱动存储和检索大文件,请参阅以下 API 文档