GridFS
在本页面上
概述
在本指南中,您可以学习如何使用 GridFS 在 MongoDB 中存储和检索大文件。GridFS 是驱动程序实现的一个规范,它描述了在存储时如何将文件分割成块,以及在检索时如何将它们重新组装。GridFS 的驱动程序实现是一个抽象层,用于管理文件存储的操作和组织。
如果您的文件大小超过 16MB 的 BSON 文档大小限制,则应使用 GridFS。有关 GridFS 是否适合您的用例的更详细信息,请参阅GridFS 服务器手册页面.
以下部分描述了 GridFS 操作及其执行方法
GridFS 的工作原理
GridFS 将文件组织在一个 存储桶 中,这是一个包含文件块及其描述信息的 MongoDB 集合组。存储桶包含以下集合,这些集合按照 GridFS 规范中定义的约定命名:
以下
chunks
集合存储二进制文件块。files
集合存储文件元数据。
当你创建一个新的GridFS存储桶时,驱动程序会创建前一个集合,以默认存储桶名称前缀fs
开头,除非你指定了不同的名称。驱动程序还会为每个集合创建索引,以确保高效检索文件和相关元数据。如果存储桶尚不存在,驱动程序仅在第一次写入操作时创建GridFS存储桶。驱动程序仅在索引不存在且存储桶为空时创建索引。有关GridFS索引的更多信息,请参阅服务器手册页面GridFS Indexes。
使用GridFS存储文件时,驱动程序将文件分割成较小的块,每个块由chunks
集合中的一个单独文档表示。它还在files
集合中创建一个文档,该文档包含文件ID、文件名和其他文件元数据。你可以从内存或从流上传文件。查看以下图表以了解GridFS在上传到存储桶时如何分割文件。

检索文件时,GridFS从指定存储桶的files
集合中获取元数据,并使用这些信息从chunks
集合中的文档重新构造文件。你可以将文件读入内存或将输出输出到流。
创建GridFS存储桶
要存储或从GridFS检索文件,请在MongoDB数据库上创建存储桶或获取现有存储桶的引用。使用MongoDatabase
实例作为参数调用GridFSBuckets.create()
辅助方法来实例化GridFSBucket
。您可以使用GridFSBucket
实例来对您存储桶中的文件调用读写操作。
MongoDatabase database = mongoClient.getDatabase("mydb"); GridFSBucket gridFSBucket = GridFSBuckets.create(database);
要创建或引用具有除默认名称fs
以外的自定义名称的存储桶,将存储桶名称作为create()
方法的第二个参数传递,如下所示
GridFSBucket gridFSBucket = GridFSBuckets.create(database, "myCustomBucket");
注意
当你调用create()
时,MongoDB不会在不存在的情况下创建存储桶。相反,MongoDB在需要时创建存储桶,例如当你上传第一个文件时。
有关本节中提到的类和方法的更多信息,请参阅以下API文档
存储文件
要将文件存储在GridFS桶中,您可以从InputStream实例上传它,或者将其数据写入GridFSUploadStream。
对于任何上传过程,您都可以指定配置信息,例如文件块大小和其他字段/值对,以存储为元数据。如下代码片段所示,在GridFSUploadOptions实例上设置此信息
GridFSUploadOptions options = new GridFSUploadOptions() .chunkSizeBytes(1048576) // 1MB chunk size .metadata(new Document("myField", "myValue"));
有关更多信息,请参阅GridFSUploadOptions API文档。
重要
使用MAJORITY写入关注
在GridFS桶中存储文件时,请确保您使用WriteConcern.MAJORITY
写入关注。如果您指定了不同的写入关注,则GridFS文件上传期间发生的副本集选举可能会中断上传过程,导致某些文件块丢失。
有关写入关注的更多信息,请参阅服务器手册中的写入关注页面。
使用输入流上传文件
本节展示了如何使用输入流将文件上传到GridFS存储桶。以下代码示例展示了如何使用FileInputStream
从您的文件系统中读取数据,并通过以下操作将其上传到GridFS:
使用
FileInputStream
从文件系统中读取。使用
GridFSUploadOptions
设置块大小。设置一个名为
type
的自定义元数据字段,将其值设置为"zip archive"。上传名为
project.zip
的文件,指定GridFS文件名为"myProject.zip"。
String filePath = "/path/to/project.zip"; try (InputStream streamToUploadFrom = new FileInputStream(filePath) ) { // Defines options that specify configuration information for files uploaded to the bucket GridFSUploadOptions options = new GridFSUploadOptions() .chunkSizeBytes(1048576) .metadata(new Document("type", "zip archive")); // Uploads a file from an input stream to the GridFS bucket ObjectId fileId = gridFSBucket.uploadFromStream("myProject.zip", streamToUploadFrom, options); // Prints the "_id" value of the uploaded file System.out.println("The file id of the uploaded file is: " + fileId.toHexString()); }
此代码示例在文件成功保存到GridFS后打印上传文件的文件ID。
有关更多信息,请参阅uploadFromStream().
使用输出流上传文件
本节向您展示如何通过写入输出流将文件上传到GridFS存储桶。以下代码示例显示了如何通过执行以下操作将数据写入GridFSUploadStream
从文件系统读取名为"project.zip"的文件到字节数组中。
使用
GridFSUploadOptions
设置块大小。设置一个名为
type
的自定义元数据字段,将其值设置为"zip archive"。将字节写入
GridFSUploadStream
,分配文件名"myProject.zip"。该流将数据读取到缓冲区中,直到达到在chunkSize
设置中指定的限制,并将其作为新块插入到chunks
集合中。
Path filePath = Paths.get("/path/to/project.zip"); byte[] data = Files.readAllBytes(filePath); // Defines options that specify configuration information for files uploaded to the bucket GridFSUploadOptions options = new GridFSUploadOptions() .chunkSizeBytes(1048576) .metadata(new Document("type", "zip archive")); try (GridFSUploadStream uploadStream = gridFSBucket.openUploadStream("myProject.zip", options)) { // Writes file data to the GridFS upload stream uploadStream.write(data); uploadStream.flush(); // Prints the "_id" value of the uploaded file System.out.println("The file id of the uploaded file is: " + uploadStream.getObjectId().toHexString()); // Prints a message if any exceptions occur during the upload process } catch (Exception e) { System.err.println("The file upload failed: " + e); }
此代码示例在文件成功保存到GridFS后打印上传文件的文件ID。
注意
如果文件上传不成功,操作将抛出异常,并且任何已上传的块都将成为孤儿块。一个**孤儿块**是GridFS chunks
集合中的一个文档,它不引用GridFS files
集合中的任何文件ID。当上传或删除操作中断时,文件块可能会成为孤儿块。要删除孤儿块,您必须使用读取操作来识别它们,并使用写入操作来删除它们。
有关更多信息,请参阅GridFSUploadStream的API文档。
检索文件信息
在本节中,您可以了解如何检索存储在GridFS桶的files
集合中的文件元数据。元数据包含关于所引用文件的信息,包括
文件的ID
文件的名称
文件的大小/长度
上传日期和时间
一个
metadata
文档,您可以在此存储其他任何信息
要从GridFS桶检索文件,请在GridFSBucket
实例上调用find()
方法。该方法返回一个GridFSFindIterable
,您可以从其中访问结果。
以下代码示例显示了如何从GridFS桶中的所有文件中检索和打印文件元数据。在从GridFSFindIterable
检索的结果中遍历的不同方式中,示例使用了一个Consumer
函数式接口来打印以下结果
gridFSBucket.find().forEach(new Consumer<GridFSFile>() { public void accept(final GridFSFile gridFSFile) { System.out.println(gridFSFile); } });
以下代码示例显示了如何检索和打印匹配查询过滤条件中指定字段的全部文件的文件名。示例还调用返回的GridFSFindIterable
上的sort()
和limit()
来指定排序顺序和最大结果数
Bson query = Filters.eq("metadata.type", "zip archive"); Bson sort = Sorts.ascending("filename"); // Retrieves 5 documents in the bucket that match the filter and prints metadata gridFSBucket.find(query) .sort(sort) .limit(5) .forEach(new Consumer<GridFSFile>() { public void accept(final GridFSFile gridFSFile) { System.out.println(gridFSFile); } });
由于metadata
是一个嵌入文档,查询过滤器使用点表示法指定文档内的type
字段。有关更多信息,请参阅服务器手册指南中的查询嵌入/嵌套文档。
有关本节中提到的类和方法的更多信息,请参阅以下资源
GridFSFindIterable API 文档
GridFSBucket.find() API 文档
下载文件
您可以直接从 GridFS 将文件下载到流中,或者从流中将其保存到内存中。您可以使用文件 ID 或文件名指定要检索的文件。
文件版本
当您的存储桶包含多个具有相同文件名的文件时,GridFS 默认选择文件的最新版本。为了区分具有相同名称的每个文件,GridFS 为具有相同文件名的文件分配一个修订号,按上传时间排序。
原始文件修订号为 "0",下一个最近的文件修订号为 "1"。您还可以指定负值,这些值对应于修订的近期程度。修订值 "-1" 指的是最近的修订,"-2" 指的是下一个最近的修订。
以下代码片段展示了如何在GridFSDownloadOptions实例中指定文件的第二个修订版本。
GridFSDownloadOptions downloadOptions = new GridFSDownloadOptions().revision(1);
有关修订版本枚举的更多信息,请参阅GridFSDownloadOptions.的API文档。
将文件下载到输出流
您可以将GridFS存储桶中的文件下载到输出流。以下代码示例展示了如何调用downloadToStream()
方法将名为"myProject.zip"的文件的第一修订版本下载到OutputStream
。
GridFSDownloadOptions downloadOptions = new GridFSDownloadOptions().revision(0); // Downloads a file to an output stream try (FileOutputStream streamToDownloadTo = new FileOutputStream("/tmp/myProject.zip")) { gridFSBucket.downloadToStream("myProject.zip", streamToDownloadTo, downloadOptions); streamToDownloadTo.flush(); }
有关此方法的更多信息,请参阅downloadToStream()的API文档。
将文件下载到输入流
您可以使用输入流将GridFS存储桶中的文件下载到内存中。您可以在GridFS存储桶上调用openDownloadStream()
方法来打开一个GridFSDownloadStream
,这是一个可以从中读取文件的输入流。
以下代码示例展示了如何将由fileId
变量引用的文件下载到内存中,并将其内容作为字符串打印出来
ObjectId fileId = new ObjectId("60345d38ebfcf47030e81cc9"); // Opens an input stream to read a file containing a specified "_id" value and downloads the file try (GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(fileId)) { int fileLength = (int) downloadStream.getGridFSFile().getLength(); byte[] bytesToWriteTo = new byte[fileLength]; downloadStream.read(bytesToWriteTo); // Prints the downloaded file's contents as a string System.out.println(new String(bytesToWriteTo, StandardCharsets.UTF_8)); }
有关此方法的更多信息,请参阅openDownloadStream(). API 文档。
重命名文件
您可以通过调用rename()
方法来更新您存储桶中GridFS文件的名字。您必须通过文件的ID而不是文件名来指定要重命名的文件。
注意
rename()
方法一次只能更新一个文件的名字。要重命名多个文件,从存储桶中检索匹配文件名的文件列表,从您想要重命名的文件中提取文件ID值,并将每个文件ID单独传递给rename()
方法的调用中。
以下代码示例展示了如何更新由 fileId
变量引用的文件的名称为 "mongodbTutorial.zip"。
ObjectId fileId = new ObjectId("60345d38ebfcf47030e81cc9"); // Renames the file that has a specified "_id" value to "mongodbTutorial.zip" gridFSBucket.rename(fileId, "mongodbTutorial.zip");
有关此方法的更多信息,请参阅rename() API 文档。
删除文件
您可以通过调用 delete()
方法从您的 GridFS 桶中删除文件。您必须通过其文件 ID 而不是文件名来指定文件。
注意
delete()
方法一次只能删除一个文件。要删除多个文件,请从桶中检索文件,从您想要删除的文件中提取文件 ID 值,并将每个文件 ID 分别传递给 delete()
方法的单独调用中。
以下代码示例展示了如何删除由 fileId
变量引用的文件
ObjectId fileId = new ObjectId("60345d38ebfcf47030e81cc9"); // Deletes the file that has a specified "_id" value from the GridFS bucket gridFSBucket.delete(fileId);
有关此方法的更多信息,请参阅 delete() API 文档。
删除 GridFS 桶
以下代码示例展示了如何删除名为 "mydb" 的数据库上的默认 GridFS 桶。要引用自定义命名的桶,请参阅本指南中关于 如何创建自定义桶。
MongoDatabase database = mongoClient.getDatabase("mydb"); GridFSBucket gridFSBucket = GridFSBuckets.create(database); gridFSBucket.drop();
有关此方法的更多信息,请参阅 drop() API 文档。
其他资源
可运行示例 GridFSTour.java 来自MongoDB Java Driver存储库。