天天看點

MongoDB+GridFS存儲檔案方案

作者:寒笛過霜天

GridFS是MongoDB的一個内置功能, 它提供一組檔案操作的API以利用MongoDB存儲檔案, GridFS的基本原理是将檔案儲存在兩個Collection中, 一個儲存檔案索引, 一個儲存檔案内容, 檔案内容按一定大小分成若幹塊,

每一塊存在一個Document中, 這種方法不僅提供了檔案存儲, 還提供了對檔案相關的一些附加屬性(比如MD5值, 檔案名等等)的存儲。

http://www.mongodb.org/display/DOCS/GridFS

http://www.mongodb.org/display/DOCS/GridFS+Specification

安裝文檔

https://docs.mongodb.com/manual/installation/

環境搭建

1.安裝mongoDb

vim /etc/yum.repos.d/mongodb.repo

如果是64bit的

[mongodb]

name=MongoDB Repository

baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/

gpgcheck=0

enabled=1

32bit的系統:

[mongodb]

name=MongoDB Repository

baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686/

gpgcheck=0

enabled=1

然後安裝, 會提示Y/N:

yum install mongo-10gen mongo-10gen-server # 此時在安裝之前會自動更新yum源 mongodb-org與mongo-10gen 10gen 是 MongoDB 原來的名字, 在源裡還留着 10gen 隻是為了舊包的維護, 現在就不要用 10gen 了

啟動:

service mongod start

檢視狀态

service mongod status

停止

service mongod stop

2.安裝nginx及nginx-gridfs

依賴庫、工具

# yum -y install pcre-devel openssl-devel zlib-devel

# yum -y install gcc gcc-c++

下載下傳nginx-gridfs源碼

# git clone https://github.com/mdirolf/nginx-gridfs.git

# cd nginx-gridfs

# git checkout v0.8

# git submodule init

# git submodule update

下載下傳nginx源碼,編譯安裝。(高版本支援不好)

# wget http://nginx.org/download/nginx-1.4.7.tar.gz

# tar zxvf nginx-1.4.7.tar.gz

# cd nginx-1.4.7

# ./configure --with-openssl=/usr/include/openssl --add-module=../nginx-gridfs/

# make -j8 && make install –j8

解釋: make -j4 作業數是在編譯的時候指定主機的CPU個數

../nginx-gridfs/ 配置成對應nginx-gridfs的路徑

3. 配置nginx-gridfs

vim /usr/local/nginx/conf/nginx.conf

在 server 節點中添加 location 節點

location /img/ {

gridfs testdb

field=filename

type=string;

mongo 192.168.0.159:27017;

}

location /files/ {

gridfs testdb

field=_id

type=objectid;

mongo 192.168.0.159:27017;

}

這裡我們的mongo服務在IP 192.168.0.159。

如果不指定 field, 預設為 MongoDB 的自增ID, 且type為int

配置參數介紹:

gridfs:nginx識别插件的關鍵字

testdb:db名

[root_collection]: 選擇collection, 如root_collection=blog, mongod就會去找blog.files與blog.chunks兩個塊, 預設是fs

[field]: 查詢字段, 保證mongdb裡有這個字段名, 支援_id, filename, 可省略, 預設是_id

[type]: 解釋field的資料類型, 支援objectid, int, string, 可省略, 預設是int

[user]: 使用者名, 可省略

[pass]: 密碼, 可省略

mongo: mongodb url

啟動nginx服務

# /usr/local/nginx/sbin/nginx

可能出現:

Nginx [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use)

這時可用使用指令關閉占用80端口的程式

sudo fuser -k 80/tcp

GridFS使用

MongoDB提供了一個指令行工具mongofiles可以來處理GridFS, 在bin目錄下。

列出所有檔案:

mongofiles list

上傳一個檔案:

mongofiles put xxx.txt

下載下傳一個檔案:

mongofiles get xxx.txt

查找檔案:

mongofiles search xxx //會查找所有檔案名中包含“xxx”的檔案

mongofiles list xxx //會查找所有檔案名以“xxx”為字首的檔案

參數說明:

–d 指定資料庫, 預設是fs, Mongofiles list –d testGridfs

-u –p 指定使用者名, 密碼

-h 指定主機

-port 指定主機端口

-c 指定集合名, 預設是fs

-t 指定檔案的MIME類型, 預設會忽略

使用MongoVUE來檢視,管理GridFS

MongoVUE位址:http://www.mongovue.com/

MongoVUE是個免費軟體, 但超過15天後功能受限。可以通過删除以下系統資料庫項來解除限制:

[HKEY_CURRENT_USER\Software\Classes\CLSID\{B1159E65-821C3-21C5-CE21-34A484D54444}\4FF78130]

把這個項下的值全删掉就可以了。

<?php

// 初始化gridfs

$conn = new Mongo(); // 連接配接MongoDB

$db = $conn->photos; // 選擇資料庫

$collection = $db->getGridFS(); // 取得gridfs對象

// gridfs有三種方式存儲檔案

// 第一種直接存儲檔案

$id = $collection->storeFile("./logo.png");

// 第二種存儲檔案二進制流

$data = file_get_contents("./logo.png");

$id = $collection->storeBytes($data,array("param" => '附加參數将随圖檔一起存入'));

// 第三種儲存直接表單送出的檔案$_FILES

$id = $collection->storeUpload('upfile');

// 相當于

$id = $collection->storeFile($_FILES['upfile']['tmp_name']);

//--------------以上是儲存圖檔--下面開始讀取圖檔----------------

// 儲存成功後傳回$id = md5字元串

$logo = $collection->findOne(array('_id'=>$id)); // 以_id為索引取得檔案

header('Content-type: image/png'); // 輸出圖檔頭

echo $logo ->getBytes(); // 輸出資料流

?>

特别備注:

通過 $id = $collection->storeFile($_FILES['upfile']['tmp_name']); 産生的ID, 是MongoDB的 ID對象, 而不是一個 字元串! 如以下格式:

{

"_id": ObjectId("525418525ba8a18c1b000001"),

"filename": "D:\\php\\xampp\\tmp\\php8116.tmp",

"uploadDate": ISODate("2013-10-08T14:36:02.0Z"),

"length": NumberInt(55862),

"chunkSize": NumberInt(262144),

"md5": "a6f19f3434f0b36bb2611cd4c6d82b35"

}

不過, 我們可以通過 $id = strval($id), 把上述 ID對象字元串化, 如可得到上述的 525418525ba8a18c1b000001 值, 再把這個值存到MySQL資料庫中, 到時候可通過這個字元串ID 作為條件, 找到相應的MongoDB資源。

參考代碼如下:

$conn = new Mongo(C('127.0.0.1:27017')); //如果設定了密碼自己配置DSN

$db=$conn->selectDB('edu_sns'); // 選擇資料庫

$collection = $db->getGridFS('zk_attach'); // 選擇集合,相等于選擇資料表

$id=$_GET['id'];

$object=$collection->findOne(array('_id'=>new MongoId($id)));

header('Content-type: image/png');

echo $object->getBytes();

完整流程

1、前端上傳檔案html index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Mongo Gridfs</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
<label for="upfile" >上傳圖檔</label>
<input type="file" id="upfile" name="upfile" />
<input type="submit" />
</form>
</body>
</html>           

2、上傳檔案進入MongoDB資料庫并傳回圖檔的索引ID upload.php

<?php
//上傳圖檔到
header("Content-type:text/html;charset=utf-8");
// 連接配接Mongo并初始化GFS
// 資料庫命名 picDB;集合命名pic_userid
$conn = new MongoClient();
$db = $conn->picDB;
// 取得gridfs對象
$prefix = 'pic';
$collection = $db->getGridFS($prefix);
// 上傳圖檔
if(isset($_FILES['upfile'])){
$id = $collection->storeUpload('upfile');
$id = strval($id);
echo "<p>圖檔路徑為:<font color=red>http://{$_SERVER['HTTP_HOST']}/image.php?id={$id}</font></p>";
}
?>           

3、根據圖檔ID直接從MongoDB裡面擷取圖檔資源并顯示 image.php

<?php
// 根據ID索引值将圖檔資源取出來,即圖檔二進制資料
$conn = new Mongo();
$db = $conn->picDB;
// 取得gridfs對象
$prefix = 'pic';
$collection = $db->getGridFS($prefix);
$id = $_GET['id'];
$object = $collection->findOne(array('_id' => new MongoId($id)));
header('Content-type:image/jpg');
echo $object->getBytes();
?>           

在upload.php裡面萬一根據ID無法取出資料! 應該做出提示。

在image.php裡面應該添加異常處理, 如果取出的資料格式不是image而是其他格式! 要提前判斷type。

最後, 要在MongoDB的使用中添加授權使用! 因為mongo預設是非授權使用的, 也就是通路資料庫時不需要提供使用者名和密碼。

參考類庫:

https://github.com/crodas/MongoFS