存储大文件
概述
在本指南中,您将学习如何通过使用 GridFS 在 MongoDB 中存储和检索大文件。GridFS 是 MongoDB PHP 库实现的一个规范,描述了在存储文件时如何将其分割成块,并在检索时重新组合它们。库对 GridFS 的实现是一个抽象,用于管理文件存储的操作和组织。
如果您的文件大小超过 16MB 的 BSON 文档大小限制,请使用 GridFS。有关 GridFS 是否适合您的用例的更多详细信息,请参阅GridFS MongoDB 服务器手册。
GridFS 的工作原理
GridFS 将文件组织在一个 桶 中,这是一个包含文件块及其描述信息的 MongoDB 集合组。桶包含以下集合,使用 GridFS 规范中定义的约定命名
集合
chunks
集合存储二进制文件块。files
集合存储文件元数据。
当您创建一个新的 GridFS 桶时,库会创建上述集合,前缀为默认的桶名称 fs
,除非您指定了不同的名称。库还会在每个集合上创建索引,以确保高效检索文件和相关元数据。只有在第一次执行写操作时,库才会创建 GridFS 桶(如果不存在)。只有在索引不存在且桶为空时,库才会创建索引。有关 GridFS 索引的更多信息,请参阅 MongoDB 服务器手册中的 GridFS 索引。
在使用GridFS存储文件时,库会将文件分割成更小的块,每个块由chunks集合中的一个单独文档表示。它还在files集合中创建一个文档,包含文件ID、文件名和其他文件元数据。您可以通过向MongoDB PHP库传递流来消费或直接创建新流并写入它来上传文件。有关流的更多信息,请参阅PHP手册中的流。
查看以下图表以了解GridFS在将文件上传到存储桶时如何分割文件

在检索文件时,GridFS从指定存储桶的files集合中检索元数据,并使用这些信息从chunks集合中的文档中重建文件。您可以通过将文件内容写入现有流或创建一个指向文件的新流来读取文件。
创建GridFS存储桶
要存储或从GridFS检索文件,请调用您数据库上的MongoDB\Database::selectGridFSBucket()
方法。此方法访问现有存储桶或如果不存在则创建新存储桶。
以下示例在db
数据库上调用selectGridFSBucket()
方法
$bucket = $client->db->selectGridFSBucket();
自定义Bucket
您可以通过传递一个数组到selectGridFSBucket()
方法来自定义GridFS bucket配置。以下表格描述了您可以在数组中设置的某些选项
选项 | 描述 |
---|---|
bucketName | 指定用于文件和块集合的前缀的bucket名称。默认值是 'fs' 。类型: string |
chunkSizeBytes | 指定GridFS拆分文件的块大小。默认值是 261120 。类型: integer |
readConcern | 指定用于bucket操作的读取关注点。默认值是数据库的读取关注点。 类型: MongoDB\Driver\ReadConcern |
readPreference | 指定用于bucket操作的读取偏好。默认值是数据库的读取偏好。 类型: MongoDB\Driver\ReadPreference |
writeConcern | 指定用于bucket操作的写入关注点。默认值是数据库的写入关注点。 类型: MongoDB\Driver\WriteConcern |
以下示例通过传递一个设置bucketName
选项的数组到selectGridFSBucket()
来创建一个名为'myCustomBucket'
的bucket
$custom_bucket = $client->db->selectGridFSBucket( ['bucketName' => 'myCustomBucket'] );
上传文件
您可以通过以下方法将文件上传到GridFS bucket
MongoDB\GridFS\Bucket::openUploadStream()
:打开一个新的上传流,您可以将文件内容写入其中MongoDB\GridFS\Bucket::uploadFromStream()
:将现有流的内溶上传到GridFS文件
写入上传流
使用openUploadStream()
方法创建指定文件名的上传流。该方法允许你在选项数组中指定配置信息,并将其作为参数传递。
以下示例使用上传流执行以下操作
为新名为
'my_file'
的GridFS文件打开可写流将
metadata
选项设置为传递给openUploadStream()
方法的数组参数调用
fwrite()
方法将数据写入流指向的'my_file'
调用
fclose()
方法关闭指向'my_file'
的流
$stream = $bucket->openUploadStream('my_file', [ 'metadata' => ['contentType' => 'text/plain'] ]); fwrite($stream, 'Data to store'); fclose($stream);
上传现有流
使用uploadFromStream()
方法将流的内容上传到新的GridFS文件。该方法允许你在选项数组中指定配置信息,并将其作为参数传递。
以下示例执行以下操作
调用
fopen()
方法以二进制读取模式(rb
)打开位于/path/to/input_file
的文件作为流调用
uploadFromStream()
方法将流的内容上传到名为'new_file'
的GridFS文件
$file = fopen('/path/to/input_file', 'rb'); $bucket->uploadFromStream('new_file', $file);
检索文件信息
在本节中,您可以学习如何检索存储在GridFS存储桶的files
集合中的文件元数据。元数据包含关于所引用文件的信息,包括
文件的
_id
文件的名称
文件长度/大小
上传日期和时间
一个
metadata
文档,您可以在此存储其他任何信息
要从GridFS存储桶检索文件,请调用MongoDB\GridFS\Bucket::find()
方法,该方法在MongoDB\GridFS\Bucket
实例上。该方法返回一个MongoDB\Driver\Cursor
实例,您可以从中访问结果。要了解更多关于MongoDB PHP库中Cursor
对象的信息,请参阅从游标中访问数据指南。
示例
以下代码示例展示了如何从GridFS存储桶中的文件检索并打印文件元数据。它使用foreach
循环遍历返回的游标并显示上传文件示例中上传的文件内容
$files = $bucket->find(); foreach ($files as $file_doc) { echo toJSON($file_doc), PHP_EOL; }
{ "_id" : { "$oid" : "..." }, "chunkSize" : 261120, "filename" : "my_file", "length" : 13, "uploadDate" : { ... }, "metadata" : { "contentType" : "text/plain" }, "md5" : "6b24249b03ea3dd176c5a04f037a658c" } { "_id" : { "$oid" : "..." }, "chunkSize" : 261120, "filename" : "new_file", "length" : 13, "uploadDate" : { ... }, "md5" : "6b24249b03ea3dd176c5a04f037a658c" }
find()
方法接受各种查询规范。您可以使用其$options
参数指定排序顺序、要返回的最大文档数以及在返回之前要跳过的文档数。要查看可用选项的列表,请参阅API文档。
注意
前面的示例调用toJSON()
方法以扩展JSON格式打印文件元数据,该格式定义如下代码
function toJSON(object $document): string { return MongoDB\BSON\Document::fromPHP($document)->toRelaxedExtendedJSON(); }
下载文件
您可以使用以下方法从GridFS桶中下载文件
MongoDB\GridFS\Bucket::openDownloadStreamByName()
或MongoDB\GridFS\Bucket::openDownloadStream()
:打开一个新的下载流,从中您可以读取文件内容MongoDB\GridFS\Bucket::downloadToStream()
:将整个文件写入现有的下载流
从下载流中读取
您可以使用 MongoDB\GridFS\Bucket::openDownloadStreamByName()
方法创建下载流来从您的MongoDB数据库中下载文件。
以下示例使用下载流执行以下操作
选择名为
'my_file'
的GridFS文件,在 写入上传流 示例中上传,并将其作为可读流打开调用
stream_get_contents()
方法读取'my_file'
的内容打印文件内容
调用
fclose()
方法关闭指向'my_file'
的下载流
$stream = $bucket->openDownloadStreamByName('my_file'); $contents = stream_get_contents($stream); echo $contents, PHP_EOL; fclose($stream);
"Data to store"
注意
如果有多个具有相同文件名的文档,GridFS将流式传输具有给定名称的最新文件(根据 uploadDate
字段确定)。
或者,您可以使用接受文件 _id
字段作为参数的 MongoDB\GridFS\Bucket::openDownloadStream()
方法
$stream = $bucket->openDownloadStream(new ObjectId('66e0a5487c880f844c0a32b1')); $contents = stream_get_contents($stream); fclose($stream);
注意
GridFS流式API无法加载部分块。当下载流需要从MongoDB中拉取一个块时,它会将整个块拉入内存。255千字节默认块大小通常足够,但您可以将块大小减少以减少内存开销,或者在工作于大文件时增加块大小。有关设置块大小的更多信息,请参阅此页面的 自定义桶 部分。
下载文件版本
当您的存储桶包含多个同名文件时,GridFS默认选择最近上传的版本。为了区分具有相同名称的每个文件,GridFS为它们分配一个按上传时间排序的修订号。
原始文件修订号为0
,下一个最近文件修订号为1
。您还可以指定与修订号新鲜度相对应的负值。修订值-1
引用最新修订,-2
引用下一个最近修订。
您可以通过向openDownloadStreamByName()
方法传递一个选项数组并指定revision
选项来指示GridFS下载特定的文件修订版。以下示例读取名为'my_file'
的原始文件内容,而不是最新修订版
$stream = $bucket->openDownloadStreamByName('my_file', ['revision' => 0]); $contents = stream_get_contents($stream); fclose($stream);
下载到现有流
您可以通过在您的存储桶上调用MongoDB\GridFS\Bucket::downloadToStream()
方法将GridFS文件的内容下载到现有流。
以下示例执行以下操作
调用
fopen()
方法以二进制写入(wb
)模式打开位于/path/to/output_file
的文件作为流将具有
_id
值ObjectId('66e0a5487c880f844c0a32b1')
的GridFS文件下载到流
$file = fopen('/path/to/output_file', 'wb'); $bucket->downloadToStream( new ObjectId('66e0a5487c880f844c0a32b1'), $file, );
重命名文件
使用 MongoDB\GridFS\Bucket::rename()
方法来更新您bucket中GridFS文件的名称。您必须通过其 _id
字段而不是文件名来指定要重命名的文件。
以下示例展示了如何通过引用文档的 _id
字段来更新 filename
字段为 'new_file_name'
。
$bucket->rename(new ObjectId('66e0a5487c880f844c0a32b1'), 'new_file_name');
注意
文件版本
rename()
方法一次只能更新一个文件的名称。如果您想要重命名每个版本,或者具有相同文件名但上传时间不同的文件,收集每个版本的 _id
值。然后,将每个值分别调用 rename()
方法。
删除文件
使用 MongoDB\GridFS\Bucket::delete()
方法从您的bucket中删除文件的集合文档和相关块,从而有效地删除文件。您必须通过其 _id
字段而不是文件名来指定文件。
以下示例展示了如何通过引用 _id
字段来删除文件。
$bucket->delete(new ObjectId('66e0a5487c880f844c0a32b1'));
注意
文件版本
delete()
方法一次只能删除一个文件。如果您想要删除每个版本,或者具有相同文件名但上传时间不同的文件,收集每个版本的 _id
值。然后,将每个值分别调用 delete()
方法。
API 文档
要了解更多关于使用 MongoDB PHP 库存储和检索大文件的信息,请参阅以下 API 文档