图片云存储的技术实现
?
文章转载自杨鑫奇的技术博客
原文地址http://www.cnblogs.com/scotoma/archive/2011/12/05/2276291.html
?
引子
最近很久没有写文章了,主要是忙于公司的项目,也给自己一个深度挖掘的方向,另外生活也出现了很大的变动,现在终于憋不住了。订阅技术博文是技术者必须做的一件事情,每天打开google reader总是会有所发现有所惊喜继而是感慨。本次主要来介绍下图片的云存储方面的技术实现,不管是对于云技术实践,还是降低相关的运维成本,都是很有效果的,不信就往下看吧!
关键字:图片云存储 又拍云存储 PHP图片存储 廉价高速图片存储
正文
上周看了磊哥的文章 又拍云实战 http://www.blogkid.net/archives/2782.html 后很有收获就觉得去试试又拍云存储的服务http://www.upyun.com/,就去注册然后申请试用了。
本次upyun.com的认证方式很让我意外,竟然是客服打电话过来确认,这点服务感觉还是挺好的,从这里感觉还是蛮重视用户的。特别要说的是,今天是星期天我又在这里宣传upyun.com的服务,就和同事说了下,结果他去注册了,竟然在半小时后就接到了客服人员的电话了,感觉这个确认还是很及时的,体验很好,这点感觉还是很不错的。
试用就开始吧,为什么要试用又拍云存储服务呢?这点磊哥的文章中就已经说的很清楚了,这里我做归纳补充下:
1,图片服务器的托管及运维费用挺高的,而且峰值带宽觉得了大部分成本,且运维的软硬成本增大
2,单机达不到CDN功效,需要CDN支持的话,花费就更大了
3,图片的处理及图片的缓存,需要配置nginx的静态缓存图片,需要做系统设计扩展图片类的保存图片及缩略图功能
4,图片的备份,对于图片的备份是个问题需要用rsync同步到备份机器,添加了运维成本和开发成本
基于以上原因,自己开发及部署图片服务的代价还是很大的,所以这个也是极力推荐使用又拍云存储www.upyun.com的原因了,至于大家说的upyun.com是否稳定,磊哥提到的猛买网用了2个月还是没问题的,我也相信@gofeeling和又拍的技术实力的。
如何申请及开通就请详细参看磊哥的博文吧,本篇主要更细致化的讲解技术实现。
首先,设计表结构
id,filename,desc,createtime,status,remoteurl,url,model,user_id
本次主要用到的字段有 filename 及 model
构造图片的访问地址 $staticUrl / $model / $filename
例如: http://img001.img.woshimaijia.com/user/testuser.jpg
考虑到图片表可能进行分表,这里的id使用了 17位的bigint 时间递增
其次,完善图片类
首先图片的处理流程分为如下
1.用户从表单中上传的图片
2.已经上传的图片
现在这2部分的图片都需要迁移到又拍云存储中,而且都需要做本地进行备份。
所以处理的逻辑如下
用户上传 --> 图片保存到WebServer --> 调用ImageUpload类将图片上传到upyun.com --> 更新数据库表
图片读取 --> 获取图片id集合 --> 调用getImage方法拼接URL --> 返回图片URL
下午调试了下实现了一些HTTP REST的图片使用类,主要包含了
上传,下载,删除,查看使用空间
具体代码如下
?
?<?php
// +----------------------------------
// | Buddy Framework
// +----------------------------------
// | Copyright (c) 2011 http://buddy.woshimaijia.com All rights reserved.
// +----------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------
// | Author: xinqiyang <xinqiyang@gmail.com>
// +----------------------------------
/**
?
* Image Upload service
?
* use upaiyun.com image service
?
* 使用了又拍云存储的图片服务
?
* @author xinqiyang
?
*
?
*/
class
ImageUpload
{
????
protected
$_config
=
array
();
????
public
function
__construct()
????
{
????????
$this
->_config = C(
'imageservice'
);
????
}
?
?????
/**
?????
* update file to upaiyun.com
?????
* 上传图片保存到又拍云存储网
?????
* @param unknown_type $object
?????
* @param unknown_type $filename
?????
* @param unknown_type $path
?????
*/
????
public
function
put(
$object
,
$filename
,
$path
)
????
{
????????
$postField
=
file_get_contents
((
realpath
(
$path
)));
????????
$process
= curl_init(
$this
->_config[
'api'
].
'/'
.
$this
->_config[
'bucketname'
].
'/'
.
$object
.
'/'
.
$filename
);
????????
curl_setopt(
$process
, CURLOPT_POST, 1);
????????
curl_setopt(
$process
, CURLOPT_POSTFIELDS,
$postField
);
????????
curl_setopt(
$process
, CURLOPT_USERPWD,
$this
->_config[
'username'
].
':'
.
$this
->_config[
'userpass'
]);
????????
curl_setopt(
$process
, CURLOPT_HTTPHEADER,
array
(
'Expect:'
,
"Mkdir:true"
));
????????
curl_setopt(
$process
, CURLOPT_HEADER, 0);
????????
curl_setopt(
$process
, CURLOPT_TIMEOUT, 30);
????????
curl_setopt(
$process
, CURLOPT_RETURNTRANSFER, 1);
????????
curl_setopt(
$process
, CURLOPT_FOLLOWLOCATION, 1);
????????
$result
= curl_exec(
$process
);
????????
$code
= curl_getinfo(
$process
, CURLINFO_HTTP_CODE);
????????
curl_close(
$process
);
????????
return
array
(
'code'
=>
$code
,
'info'
=>
$result
);
????
}
?
?????
public
? function
get(
$object
,
$filename
)
????
{
????????
$process
= curl_init(
$this
->_config[
'api'
].
'/'
.
$this
->_config[
'bucketname'
].
'/'
.
$object
.
'/'
.
$filename
);
????????
curl_setopt(
$process
, CURLOPT_USERPWD,
$this
->_config[
'username'
].
':'
.
$this
->_config[
'userpass'
]);
????????
curl_setopt(
$process
, CURLOPT_HEADER, 0);
????????
curl_setopt(
$process
, CURLOPT_TIMEOUT, 30);
????????
curl_setopt(
$process
, CURLOPT_RETURNTRANSFER, 1);
????????
curl_setopt(
$process
, CURLOPT_FOLLOWLOCATION, 1);
????????
$result
= curl_exec(
$process
);
????????
$code
= curl_getinfo(
$process
, CURLINFO_HTTP_CODE);
????????
curl_close(
$process
);
????????
return
array
(
'code'
=>
$code
,
'info'
=>
$result
);
????
}
?
?????
public
? function
delete
(
$object
,
$filename
)
????
{
????????
$process
= curl_init(
$this
->_config[
'api'
].
'/'
.
$this
->_config[
'bucketname'
].
'/'
.
$object
.
'/'
.
$filename
);
????????
curl_setopt(
$process
, CURLOPT_CUSTOMREQUEST,
'DELETE'
);
????????
curl_setopt(
$process
, CURLOPT_USERPWD,
$this
->_config[
'username'
].
':'
.
$this
->_config[
'userpass'
]);
????????
curl_setopt(
$process
, CURLOPT_HEADER, 0);
????????
curl_setopt(
$process
, CURLOPT_TIMEOUT, 30);
????????
curl_setopt(
$process
, CURLOPT_RETURNTRANSFER, 1);
????????
curl_setopt(
$process
, CURLOPT_FOLLOWLOCATION, 1);
????????
$result
= curl_exec(
$process
);
????????
$code
= curl_getinfo(
$process
, CURLINFO_HTTP_CODE);
????????
curl_close(
$process
);
????????
return
array
(
'code'
=>
$code
,
'info'
=>
$result
);
????
}
?
?????
public
? function
usage()
????
{
?????????
$process
= curl_init(
$this
->_config[
'api'
].
'/'
.
$this
->_config[
'bucketname'
].
'?usage'
);?
?????????
curl_setopt(
$process
, CURLOPT_USERPWD,
$this
->_config[
'username'
].
':'
.
$this
->_config[
'userpass'
]);
?????????
curl_setopt(
$process
, CURLOPT_HEADER, 0);?
?????????
curl_setopt(
$process
, CURLOPT_TIMEOUT, 30);?
?????????
curl_setopt(
$process
, CURLOPT_RETURNTRANSFER, 1);?
?????????
$result
= curl_exec(
$process
);
????????
$code
= curl_getinfo(
$process
, CURLINFO_HTTP_CODE);
????????
curl_close(
$process
);
????????
return
array
(
'code'
=>
$code
,
'info'
=>
$result
);
????
}
?????
?????
/**
?????
* save file or image to upaiyun.com then update db
?????
* 先保存数据到又拍云存储,然后在更新数据库
?????
* @param string $model
?????
* @param string $filename?? 保存文件名
?????
* @param mixed $streamFile? 流文件或者是图片路径
?????
* @param bigint $id ID
?????
* @param bigint $user_id userid
?????
*/
????
public
? function
save(
$model
,
$filename
=
''
,
$streamFile
=
''
,
$id
=
''
,
$user_id
)
????
{
????????
$result
=
$this
->put(
$model
,
$filename
,
$streamFile
);
????????
$id
=
empty
(
$id
) ? objid() :
$id
;
????????
$filename
=
empty
(
$filename
) ?
$id
:
$filename
;
????????
if
(
$result
[
'code'
] == 200)
????????
{
????????????
$array
=
array
(
????????????????
'id'
=>
$id
,
????????????????
'filename'
=>
$filename
,
????????????????
'model'
=>
$model
,
????????????????
'status'
=> 0,
????????????????
'user_id'
=>
$user_id
,
????????????
);
????????????
return
ImageService::add(
$array
);
????????
}
????????
return
$result
;
????
}
?????
?????
//从缓存获取image表中的 model filename 然后拼接成url 返回
????
public
? function
? display(
$id
)
????
{
????????
$url
=
$this
->_config[
'url'
];
????????
return
$this
->_config[
'url'
].ImageService::getImage(
$id
);
????
}
?
??
?}
结语
其实使用API的方式操作其实会比现在的繁琐一些,保存做WebServer上的文件只是作为一个备份文件的形式存在,等同步到备份地址后删除即可,对于同步到备份服务器后即可进行缩略图的操作,或者是通过upyun的url将用到的图片做download一份到备份服务器即可,另外又拍的存储的价格现在看来还是挺实惠的,感觉非常的不错,推荐大家试用。其他国内更多的靠谱的云产品的出现!