一、GridFS是什么 & 为什么需要它

  我们知道目前MongoDB的BSON文件最大只能是16M,也就是说单个文档最多只能存储16M的数据,那么如果需要MongoDB存储超过16M的大文件该怎么办呢?这就需要通过MongoDB的GridFS规范来实现了。

  GridFS并不是MongoDB自身的特性,只是一种将大型文件存储在MongoDB的文件规范,借助GridFS,我们可以很好地管理存储在MongoDB中的大文件。由于GridFS只是标准MongoDB框架下存储文件的一种不同的方式而已,所以也是受BSON最大文件大小限制的,单个文档最大不能超过16M。GridFS使用两个集合来存储文件:fs.files和fs.chunks,fs.files用于存储文件名称和大小等元数据,fs.chunks则用于存储文件内容,fs.chunks中每个文档最大为256K,大文件内容被分割成多个块存储于多个fs.chunks文档中,每个fs.chunks文档都存储了文件id(files中的_id键值)和序号(属于文件内容的第几个块)。查看文件时,先查出文件在fs.files中的文档,再根据其ID在fs.chunks中查询存储该文件内容的文档,根据其序号先后顺序进行拼凑,即可读取整个文件的内容了。

二、命令行操作GridFS

  可以使用MongoDB安装目录下的bin子目录中的mongofiles命令行工具来直接操作GridFS。

1. 存储文件:

  mongofiles存储文件使用put命令。这里我使用MongoDB的一本电子书作为例子,文件大小为56.9M。

注意:GridFS可以添加相同文件名的文件,而且就算两个文件完全相同,得到的md5值一样,GridFS还是会当成新的文件进行存储,而不会更新原有文件。

2. 查询文件列表:

  mongofiles查询文件列表使用list命令。

  使用mongo命令行工具查询该文件在MongoDB中的存储内容(数据库为test):

(上图中fs.chunks文档只是截取了前几个而已,图中注明了fs.files和fs.chunks集合中文档的字段含义,其中fs.hunks还有一个data字段是用于存储文件二进制数据的,这里为了避免输出一大堆的二进制数据,没有把这个字段查出来。)

3. 搜索文件:

  mongofiles查询文件列表使用search命令(模糊搜索)。

4. 删除文件:

  mongofiles查询文件列表使用delete命令(需要输入确切文件名)。

这时再使用mongofile list命令查询发现已经没有这个文件了,集合fs.files和fs.chunks也都已经没有存储该文件的任何文档了。

注意:delete命令是基于文件名删除文件的,将会删除所有同名的文件。

5. 读取文件:

  mongofiles使用get命令读取文件内容,把MongoDB中存储的某个文件的内容读取出来,写入一个文件中。

(这个命令会把MongoDB中存储的名为“d:\mongodb\files\MongoDB.pdf”的文件内容读取出来输出到文件d:\mongodb\files\MongoDB.pdf中,若文件已存在则会被覆写。)

三、php操作GridFS

MongoDB的PHP驱动也支持使用GridFS进行大文件存储。

1. 存储文件:

  这里以存储一张图片为例:

$mongo = new mongoClient('mongodb://localhost:27017');//连接MongoDB(ip:port)
$db = $mongo->cdn;//选择一个数据库来存储文件,这里选择数据库cdn
$gridfs = $db->getGridFS();//获取MongoGridFS对象
$ret = $gridfs->storeFile('/data/wwwroot/cdn/myPhoto.jpg');//存储文件
echo '<pre>';
print_r($ret);

  若文件存储成功,将返回一个MongoId对象,打印出来是这样的:

这个MongoId对象就是文件存储在fs.files集合中的文档的_id键的值,我们可以把’$id’键对应的这个字符串记录起来,以便以后可以根据这个字符串读取这个文件。注意不能根据文件名来读取文件,因为可能存在多个同名文件,只有这个_id键才是唯一的。

  除了storeFile()方法,你也可以使用storeBytes()方法存储文件的二进制流:

$data = file_get_contents('/data/wwwroot/cdn/ycl.png');
$ret = $gridfs->storeBytes($data, array('desc'=>'这是ycl的照片,哈哈!', 'info' => '无可奉告'));//可以利用第二个参数添加一些额外信息

  若文件是用户上传的,还可以使用storeUpload()方法直接存储用户上传的文件,该函数的参数就是html页面中上传文件的表单字段名称:

$ret = $gridfs->storeUpload('photo');
或:
$ret = $gridfs->storeFile($_FILES['photo']['tmp_name']);

2. 文件的查询和读取:

  首先我们查询一下刚刚存储的这张图片在fs.files集合中是怎么存储的,这里就利用刚才返回的MongoId对象进行查看:

$mongo = new mongoClient('192.168.97.200:27020');
$db = $mongo->cdn;
$collection = $db->selectCollection('fs.files');
$doc = $collection->findOne(['_id' => new MongoId('5a73e86f2ff2de2207a4bd2d')]);
echo '<pre>';
print_r($doc);

  打印出来是这样的:

(若是利用storeBytes()方法进行存储并且添加了额外信息的,’filename’字段将不复存在,被添加的额外信息字段取代)

  下面直接读取这个文件,在浏览器上输出来看看:

$mongo = new mongoClient('mongodb://localhost:27017');
$db = $mongo->cdn;//选择存储文件的数据库
$gridfs = $db->getGridFS();
$doc = $gridfs->findOne(['_id' => new MongoId('5a73e86f2ff2de2207a4bd2d')]);
header('Content-type: image/jpg');//输出图片头
echo $doc->getBytes();//输出数据流

以上代码运行即可在浏览器上看到存储的图片了。

  所以,使用php操作GridFS存储和读取大文件就是这么简单,你只需要调用MongoGridFS类提供的存储文件的方法进行存储,然后将返回的MongoId对象的字符串记录下来,作为读取文件方法的参数进行文件读取就可以了。你甚至完全不必关心文件在fs.files和fs.chunks两个集合中是怎么存储的,读取文件的时候又需要怎么关联查询,这些统统不用你操心!

  当然,如果你不嫌麻烦,完全可以自己查询fs.chunks集合进行文件读取:

$mongo = new mongoClient('mongodb://localhost:27017');
$db = $mongo->cdn;
$collection = $db->selectCollection('fs.chunks');
$cursor = $collection->find(['files_id' => new MongoId('5a74206f2ff2de1c07a4bd2d')])->sort(['n' => 1]);
header('Content-type: application/pdf');
while($chunk = $cursor->getNext()) {//逐个chunk输出,避免内存超出
echo $chunk['data']->bin;
}

这种读取方式虽然麻烦了些,但是性能上比使用MongoGridFS类进行查询要好一些,我自己进行了一下测试,读取并在浏览器显示一个56.9M的pdf文档,使用MongoGridFS类读取花费8秒多,而使用下面这种方法只要6秒多,节省了2秒钟的时间!

3. 删除文件:

  删除MongoDB中存储的文件使用delete()方法:

$mongo = new mongoClient('mongodb://localhost:27017');
$db = $mongo->cdn;
$gridfs = $db->getGridFS();
$ret = $gridfs->delete(new MongoId('5a73e86f2ff2de2207a4bd2d'));
echo '<pre>';
print_r($ret);

  当然,还有remove()方法也可以进行文件的删除。

MongoDB GridFS(命令行+php操作)的更多相关文章

  1. MongoDB 基础命令行

    本文专门介绍MongoDB的命令行操作.其实,这些操作在MongoDB官网提供的Quick Reference上都有,但是英文的,为了方便,这里将其稍微整理下,方便查阅. 登录和退出 mongo命令直 ...

  2. [转]Mysql命令行常用操作

    Mysql命令行常用操作 一.从命令行登录MySQL数据库服务器 1.登录使用默认3306端口的MySQL /usr/local/mysql/bin/mysql -u root -p 2.通过TCP连 ...

  3. SLAM+语音机器人DIY系列:(一)Linux基础——3.Linux命令行基础操作

    摘要 由于机器人SLAM.自动导航.语音交互这一系列算法都在机器人操作系统ROS中有很好的支持,所以后续的章节中都会使用ROS来组织构建代码:而ROS又是安装在Linux发行版ubuntu系统之上的, ...

  4. eos开发(二)使用cleos命令行客户端操作EOS(钱包wallet基础操作)

    不知道下边这一段英文你们是不是能看懂,如果看不懂那就算了,我就是转过来随便看看的. 总之你记住nodeos.cleos和keosd这三个工程十分重要就行了,回头咱们的研究都从这三个工程杀进去. EOS ...

  5. 使用cmd命令行窗口操作SqlServer

    本文主要介绍使用windows下的使用cmd命令行窗口操作Sqlserver, 首先我们可以运行 osql  ?/   ,这样就把所有可以通过CMD命令行操作sqlserver的命令显示出来 (有图有 ...

  6. Hadoop HDFS的shell(命令行客户端)操作实例

    HDFS的shell(命令行客户端)操作实例 3.2 常用命令参数介绍 -help 功能:输出这个命令参数手册 -ls                  功能:显示目录信息 示例: hadoop fs ...

  7. EOS开发基础之二:使用cleos命令行客户端操作EOS(钱包wallet基础操作)

    不知道下边这一段英文你们是不是能看懂,如果看不懂那就算了,我就是转过来随便看看的. 总之你记住nodeos.cleos和keosd这三个工程十分重要就行了,回头咱们的研究都从这三个工程杀进去. EOS ...

  8. HDFS shell命令行常见操作

    hadoop学习及实践笔记—— HDFS shell命令行常见操作 附:HDFS shell guide文档地址 http://hadoop.apache.org/docs/r2.5.2/hadoop ...

  9. 命令行高效操作Git,看这篇就够了

    原文地址:http://blog.jboost.cn/2019/06/16/use-git.html 对于软件开发人员来说,git几乎是每天都需要接触的工具.但对于相处如此亲密的工作伙伴,你对它的了解 ...

随机推荐

  1. GRANT - 定义访问权限

    SYNOPSIS GRANT { { SELECT | INSERT | UPDATE | DELETE | RULE | REFERENCES | TRIGGER } [,...] | ALL [ ...

  2. Hermite 矩阵及其特征刻画

    将学习到什么 矩阵 \(A\) 与 \(\dfrac{1}{2}(A+A^T)\) 两者生成相同的二次型,而后面那个矩阵是对称的,这样以来,为了研究实的或者复的二次型,就只需要研究由对称矩阵生成的二次 ...

  3. graphviz 布局和子图,表格教程

    有了这三个利器,就搞定架构图了. 子图间互相调用要开启 http://graphviz.org/pdf/dotguide.pdf

  4. shell脚本,计算输入给定的数,判断最大值,最小值,总和?

    [root@localhost ~]# cat five.sh #!/bin/bash #任意输入5个数,判断最大值,最小值,总和 s= read -p "please input:&quo ...

  5. 各种排序算法(JS实现)

    目录: 直接插入排序.希尔排序.简单选择排序.堆排序.冒泡排序.快速排序,归并排序.桶排序.基数排序.多关键字排序.总结 JS测试代码 function genArr(){ let n = Math. ...

  6. 如何用纯 CSS 创作一支诱人的冰棍

    效果预览 在线演示 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/vrxzMw 可交互视频教 ...

  7. Taro:使用taro完成小程序开发

    前言:taro是一个可以很好实现一次开发,多端统一的框架,本文只介绍它小程序端开发的一些内容.小程序项目搭建gitup已经有很清楚的说明:https://github.com/NervJS/taro ...

  8. Python模块概念

    补充:生成器表达式 将列表生成器的中括号改为小括号就是生成器表达式 res = [i for i in range(10) if i > 5]  #  列表生成式 res = (i for i ...

  9. LeetCode(39) Combination Sum

    题目 Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C w ...

  10. ios开发中遇到的文件和字符的问题大总结

    我今天遇到的NSString问题 今天遇到一个字符串空指针问题,让我明白了许多 其实我们定义一个NSString * string,其实是定义了一个字符串指针,现在string没有指向任何地方,我们必 ...