基于MongoDb的S3實現

jopen 14年前發布 | 25K 次閱讀 MongoDB NoSQL數據庫

原理是利用MongoDb的GridFS,伸展性方面交由MongoDb的auto sharding去實現,這里用PHP給MongoDb綁了個S3出來,支持選擇文件存儲節點,支持文件分目錄存儲,這樣的好處是對于一些受時間影響比較 明顯的文件,可以按照年月的形式存儲,減輕歷史包袱。

首先,配置MongoDb GridFS節點信息:

<?php
$s3Config = array(
    'foo' => array(
        'server' => '127.0.0.1',
        'database' => 'test',
        'user' => 'test',
        'password' => 'foobar',
        'domain' => 'http://s3.foobar.com'
    ),
 
    'bar' => array(
        'server' => '127.0.0.1',
        'database' => 'test',
        'user' => 'test',
        'password' => 'foobar',
        'domain' => 'http://s3.foobar.com'
    ),
);

MongoDb的S3綁定:

<?php
/**
 * 統一文件存儲
 *
 */
class Api_S3
{
    protected $_node;
 
    protected $_dir;
 
    protected $_config;
 
    /**
     * 構造函數
     *
     * @param string $node
     * @param string $dir
     * @param array $config
     */
    public function __construct($node, $dir = null, $config = null)
    {
        $this->_config = $config;
 
        $this->path($node, $dir, false);
    }
 
    /**
     * 設置文件路徑
     *
     * @param string $node
     * @param string $dir
     * @return Api_S3
     */
    public function path($node, $dir, $connect = true)
    {
        $this->_node = $node;
        $this->_dir = empty($dir) ? 'fs' : $dir;
 
        if (empty($this->_config[$this->_node])) {
            throw new Cola_Exception('Api_S3: invalidate node');
        }
 
        if ($connect) {
            $this->_gridFS = $this->_gridFS();
        }
 
        return $this;
    }
 
    /**
     * GridFS
     *
     * @return MongDbGridFS
     */
    protected function _gridFS()
    {
        $mongo = new Cola_Com_Mongo($this->_config[$this->_node]);
 
        return $mongo->gridFS($this->_dir);
    }
 
    /**
     * 獲得文件句柄
     *
     * @param string $name
     * @return MongoGridFSFile
     */
    public function file($name)
    {
        if (empty($this->_gridFS)) {
            $this->_gridFS = $this->_gridFS();
        }
 
        return $this->_gridFS->findOne(array('filename' => $name));
    }
 
    /**
     * 獲得文件內容
     *
     * @param string $name
     */
    public function read($name)
    {
        $file = $this->file($name);
 
        return $file->getBytes();
    }
 
    /**
     * 寫入文件
     *
     * @param string $name
     * @param string $data
     * @param array $extra
     * @param boolean $overWrite
     * @return boolean
     */
    public function write($name, $data, $extra = array(), $overWrite = false)
    {
        $extra = (array)$extra + array('filename' => basename($name));
 
        if ($filetype = $this->_type($name)) {
            $extra['filetype'] = $filetype;
        }
 
        if ($this->file($extra['filename'])) {
            if ($overWrite) {
                $this->delete($extra['filename']);
            } else {
                throw new Cola_Exception('Api_S3: file exists');
            }
        }
 
        return $this->_gridFS->storeBytes($data, $extra);
    }
 
    /**
     * 復制系統文件
     *
     * @param string $file
     * @param array $extra
     * @param boolean $overWrite
     * @return boolean
     */
    public function copy($file, $extra = array(), $overWrite = false)
    {
        $extra = (array)$extra + array('filename' => basename($file));
 
        if ($filetype = $this->_type($file)) {
            $extra['filetype'] = $filetype;
        }
 
        if ($this->file($extra['filename'])) {
            if ($overWrite) {
                $this->delete($extra['filename']);
            } else {
                throw new Cola_Exception('Api_S3: file exists');
            }
        }
 
        return $this->_gridFS->storeFile($file, $extra);
    }
 
    /**
     * 刪除文件
     *
     * @param string $name
     * @return boolean
     */
    public function delete($name)
    {
        if (empty($this->_gridFS)) {
            $this->_gridFS = $this->_gridFS();
        }
 
        return  $this->_gridFS->remove(array('filename' => $name));
    }
 
    /**
     * 獲得文件地址
     *
     * @param string $name
     * @param string $default
     * @return string
     */
    public function getUrl($name, $default = false)
    {
        $data = array(
            'domain' => rtrim($this->_config[$this->_node]['domain'], '/'),
            'path'   => $this->_node . (('fs' == $this->_dir) ? '' : $this->_dir),
            'name'   => $name
        );
        return  implode('/', $data);
    }
 
    /**
     * 設置文件屬性
     *
     * @param string $name
     * @param array $attr
     * @return boolean
     */
    public function setAttr($name, $attr)
    {
        if (!$file = $this->file($name)) {
            throw new Cola_Exception('Api_S3: file not exists');
        }
 
        $file->file = $attr + $file->file;
 
        return $this->_gridFS->save($file->file);
    }
 
    /**
     * 獲得文件屬性
     *
     * @param string $name
     * @return array
     */
    public function getAttr($name)
    {
        $file = $this->file($name);
        return $file->file;
    }
 
    /**
     * 獲得文件類型
     *
     * @param string $file
     * @return string
     */
    protected function _type($file)
    {
        return mime_content_type($file);
    }
}

文件存入,支持自選節點,自定義目錄,自定義文件名,可以自動添加文件類型:

<?php
$s3 = new Api_S3($node, $dir, $s3Config);
$s3->copy($file, array('filename' => $name, 'filetype' => $type));

文件讀取,以”http://s3.foobar.com/foo/201005/foobar.jpg”為例,foo映射到節點名,201005映射到目錄名,foobar.jpg映射到文件名:

<?php
$s3 = new Api_S3($node, $dir, $s3Config);
$file = $s3->file($name);
 
Cola_Response::lastModified($file->file['uploadDate']->sec);
Cola_Response::etag($file->file['md5']);
 
if (isset($file->file['filetype'])) {
    header("Content-Type: {$file->file['filetype']}");
}
 
echo $file->getBytes();

注意到我們利用了文件的修改時間設置http頭的last modified,以及用文件的md5信息設置etag值,這樣的好處是可以大大減少帶寬使用,當然,你也可以設置expire時間來減少重復請求。

關于性能問題,可以在PHP讀取的上一層,加一個Squid之類的反向代理服務,基本上就不會有問題。

轉自:http://www.fuchaoqun.com/2010/05/s3-on-mongodb-with-php/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!