现在做的这个应用,可以让用户查看久远的商品及相关的广告之类的活动。遇到一个问题,就是乐天上的图片可能失效。因此想把商品图片存至本地。由于都是一堆小图片,看资料上说FastDFS特别适合存储大量大小在4KB与500MB之间的文件。
FastDFS的文档做得不是很好。网上搜到的资料基本上说得都差不多。有时间的话还是要看一下源代码,不过是纯C写的……(鄙视一下自己。)是应用级的分布式文件存储服务。Google FS以及FastDFS、mogileFS、HDFS、TFS这些类Google FS都是应用级的,不是系统级。所以只能通过专有的API对文件进行存取访问,不支持POSIX接口,不能mount使用。
#结构
FastDFS中分为两个角色:Tracker server和Storage server。Tracker server是中心节点,负责负载均衡和调度。Tracker server在内存中记录分组和Storage server的状态,不记录文件索引信息,在客户端和Storage server访问时给出应答,所以Tracker占用的内存很小。Storage server直接利用OS的文件系统存储文件。FastDFS不会对文件进行分块存储,客户端上传的文件和Storage server上的文件一一对应。
FastDFS采用了分组存储方式。集群由group组成,集群的存储总量为所有group的存储容量之和。同一个group内的Storage server之间是互备关系,相互连接,文件完全一致。Tracker server之间相互独立,没有直接联系。Storage server会连接所有的Tracker server,向它们报告状态。
Storage server采用binlog文件记录文件上传、更新等操作。binlog中只记录文件名,不记录文件内容。
#安装
先确保已安装libevent。然后:
1 2 3 4
| wget http://fastdfs.googlecode.com/files/FastDFS_v3.03.tar.gz tar -zxf FastDFS_v3.03.tar.gz cd FastDFS vi make.sh
|
确保WITH_HTTPD=1,以支持http。
1 2
| ./make.sh ./make.sh install
|
安装完成后,先启动Tracker server,再启动Storage server。
#使用
##上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private function uploadImg($filename) { if (!file_exists($filename)) { return ''; } $pathParts = pathinfo($filename); $fileData = file_get_contents($filename); try { $dfsUrl = fastdfs_storage_upload_by_filebuff1($fileData, $pathParts['extension']); } catch (Exception $e) { echo $e->getMessage(); return ''; } unset($fileData, $pathParts); if (!unlink($filename)) { echo "Failed to delete file: $filename" . date('Y-m-d H:i:s') . "\n"; } return '/' . $dfsUrl; }
|
##用户站点使用
这里有坑,本来很自然的想法是,我已经传到FastDFS机器上了,还得到了访问地址,那岂不是理所当然地就能访问到了?后来发现居然后上传之后FastDFS认为文件不存在的!只好在使用之前先判定一下图片是否可用:
1 2 3
| public static function canUseDfsImg($dfsImg) { return $dfsImg && function_exists('fastdfs_storage_file_exist1') && function_exists('fastdfs_storage_download_file_to_buff1') && fastdfs_storage_file_exist1($dfsImg); }
|
在controller中加一个取图片的action:
1 2 3 4 5 6 7 8 9 10 11 12
| public function actionFetchPic() { $request = Yii::app()->request; $dfsImg = $request->getParam('img'); if (!empty($dfsImg)) { $realDfsImg = substr($dfsImg, 1); $pathInfo = pathinfo($dfsImg); ob_end_clean(); header("Content-type: image/" . $pathInfo['extension']); $dfsImg = fastdfs_storage_download_file_to_buff1($realDfsImg); echo $dfsImg; } }
|
然后在页面中使用:
1 2 3 4 5
| <?php if (Util::canUseDfsImg($value['local_img'])): ?> <?= CHtml::image(Yii::app()->createUrl('item/fetchPic', array('img' => $value['local_img'])), "", array('width' => 60)) ?> <?php else: ?> <?= CHtml::image($value['img'], "", array('width' => 60)); ?> <?php endif; ?>
|