基于MongoDb的S3实现

jopen 12年前
     <p>原理是利用MongoDb的GridFS,伸展性方面交由MongoDb的auto sharding去实现,这里用PHP给MongoDb绑了个S3出来,支持选择文件存储节点,支持文件分目录存储,这样的好处是对于一些受时间影响比较 明显的文件,可以按照年月的形式存储,减轻历史包袱。</p>    <p>首先,配置MongoDb GridFS节点信息:</p>    <div>     <div>      <pre><?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'     ), );</pre>     </div>    </div>    <p>MongoDb的S3绑定:</p>    <div>     <div>      <pre><?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);     } }</pre>     </div>    </div>    <p>文件存入,支持自选节点,自定义目录,自定义文件名,可以自动添加文件类型:</p>    <div>     <div>      <pre><?php $s3 = new Api_S3($node, $dir, $s3Config); $s3->copy($file, array('filename' => $name, 'filetype' => $type));</pre>     </div>    </div>    <p>文件读取,以”http://s3.foobar.com/foo/201005/foobar.jpg”为例,foo映射到节点名,201005映射到目录名,foobar.jpg映射到文件名:</p>    <div>     <div>      <pre><?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();</pre>     </div>    </div>    <p>注意到我们利用了文件的修改时间设置http头的last modified,以及用文件的md5信息设置etag值,这样的好处是可以大大减少带宽使用,当然,你也可以设置expire时间来减少重复请求。</p>    <p>关于性能问题,可以在PHP读取的上一层,加一个Squid之类的反向代理服务,基本上就不会有问题。</p>    <p>转自:http://www.fuchaoqun.com/2010/05/s3-on-mongodb-with-php/</p>