网站如果有很多用户上传图片(相册,商品图片),一般的做法是将用户图片保存在磁盘上面(数据库中记录图片的地址)。用户上传的时候按照原图、中图、小图等各个尺寸都生成一份保存在磁盘上。比如php的网店系统echsop就是这么做的,而shopex之类也大同小异。
这种做法也不是不可以。多生成几个尺寸,在磁盘上无非多存储几份而已,磁盘现在也便宜。
不过,有个问题:运营部经常需要很多尺寸版本的图片,比如需要80*80,又需要70*80,各个版面随着活动的需要,尺寸往往不同。有些一张图片可能需要根据不同的应用场景提供的图片尺寸不同,假设统计一下有几十种不同尺寸的缩略图。难道生成20张保存在磁盘上?磁盘价格固然白菜价。但是要考虑大量图片请求导致的磁盘读写的性能问题。
后来发现,随着流量大,尤其并发访问大,图片非常多的时候,频繁的读写,图片存储在磁盘上,磁盘磁头频繁定位造成的延时。
后面我了解到,业界比较成熟的做法是实时生成缩略图。也就是传递尺寸,当时就生成(生成也可以保存在磁盘上,以备下回需要的时候继续使用,也可以不保存,自己控制),这样子在磁盘上就不用保存很多份了。
看过淘宝网对自己图片存储的介绍:http://news.cnblogs.com/n/73189/
了解到如下关键点:
淘宝网的图片文件数量达到286亿多个,这些图片文件包括根据原图生成的缩略图。平均图片大小是17.45K;8K以下图片占图片数总量的61%,占存储容量的11%。
这就给淘宝网的系统带来了一个巨大的挑战,众所周知,对于大多数系统来说,最头疼的就是大规模的小文件存储与读取,因为磁头需要频繁的寻道和换道,因此在读取上容易带来较长的延时。在大量高并发访问量的情况下,简直就是系统的噩梦。
实时生成缩略图的好处总结如下:
1、节省存储空间。虽然现在磁盘确实很便宜。1g的成本很低。但是毕竟还是需要钱的。更关键一点是,图片保存在磁盘上读取时磁头频繁定位的性能问题(服务器实时生成消耗的应该是cpu资源,cpu速度很快)
图片越多时,问题越大。
2、更加灵活响应运营部的多尺寸图片需求。比如有些一张图片可能需要根据不同的应用场景提供的图片尺寸不同,假设统计一下有几十种不同尺寸的缩略图。难道生成20张保存在磁盘上?
程序员确实不用纠结保存多少份的问题了。
有些图片尺寸,只会用到一次,比如做活动使用一次,后面可能永远都不会被用到。没必要在上传图片的时候消耗cpu资源去生成,浪费磁盘空间去存储
还是一样,图片种类越多,这个问题越明显。图片就那么点点就没问题(比如就是存储用户头像而已,而商品图片就会用到很多尺寸的版本)
淘宝网提到了他们使用GraphicsMagick进行图片处理。
搜索到的资料如下
1、ImageMagick:ImageMagick是一套功能强大、稳定而且开源的工具集和开发包,可以用来读、写和处理超过89种基本格式的图片文件
利用ImageMagick,你可以根据web应用程序的需要动态生成图片, 还可以对一个(或一组)图片进行改变大小、旋转、锐化、减色或增加特效等操作,并将操作的结果以相同格式或其它格式保存,对图片的操作,即可以通过命令行进行,也可以用C/C++、Perl、Java、PHP、Python或Ruby编程来完成。
功能如下:
1. 将图片从一个格式转换到另一个格式,包括直接转换成图标。
2. 改变尺寸、旋转、锐化(sharpen)、减色、图片特效
3. 缩略图片的合成图( a montage of image thumbnails)
4. 适于web的背景透明的图片
5. 将一组图片作成gif动画,直接convert
6. 将几张图片作成一张组合图片,montage
7. 在一个图片上写字或画图形,带文字阴影和边框渲染。
8. 给图片加边框或框架
9. 取得一些图片的特性信息
2、 GraphicsMagick
GraphicsMagick 支持大图片的处理,并且已经做过GB级别的图像处理实验。GraphicsMagick能够动态的生成图片,特别适用于互联网的应用。可以用来处理调整尺寸、旋转、加亮、颜色调整、增加特效等方面
也支持C、C++、Perl、PHP、Tcl、 Ruby等的调用。事实上,GraphicsMagick是从 ImageMagick 5.5.2 分支出来的,但是现在他变得更稳定和优秀,下面就是两个之间的一些比较。
GM更有效率(测评),能更快的完成处理工作
GM更小更容易安装
GM已经被Flickr和Etsy使用,每天处理百万计的图片
GM与已经安装的软件不会发生冲突
GM几乎没有安全问题
GM的手册非常丰富
使用GraphicsMagick常见的做法是:单独一台图片服务器用来响应生成缩略图的请求。nginx+GraphicsMagick+lua结合起来。图片服务器上安装nginx,nginx调用lua程序,让lua程序来执行shell命令(GraphicsMagick原本就是可以通过shell命令来调用的,这里就是让lua来调用),大体像下面这样子:
location ~ '/images/([0-9a-z]+)_([0-9]+)x([0-9]+).jpg$' {
root /home/images;
set $image_root = '/home/images';
set $fileName = ngx.arg[1];
set $width = ngx.arg[2];
set $height = ngx.arg[3];
set $origin = $image_root/$fileName.jpg
set $file = $image_root/$fileName_$widthx$height.jpg
#请求的图片尺寸不存在 ,就实时生成,并且保存一份到磁盘上备下回使用
if (!-f $file) {
rewrite_by_lua '
local command = "gm convert "..ngx.var.origin.." -thumbnail "..ngx.var.width.."x"
..ngx.var.height.." "..ngx.var.file;
os.execute(command);
';
# rewrite_by_lua右边的单引号里面是lua程序语法。里面关键就是os.execute执行一条命令。这条命令就是调用GraphicsMagick
}
这里有篇好文章,专门分析亚马逊是如何保存图片的(其实也是实时生成缩略图的方式):
http://aaugh.com/imageabuse.html
不过是纯英文的。
其实大部分网站通过实时生成图片应该能够满足需求了。达到淘宝那样子需要研发自己的cdn图片网络环境,很少公司达到。
有个理论性的东西比较重要:
对数据库的读/写的速度永远都赶不上文件系统处理的速度。所以把图片文件丢到磁盘上让文件系统来维护比较好。只是磁盘频繁的定位造成的速度慢又是一个问题,呵呵。不过,只是并发访问量大的时候才会出现。没到极限不必担心,避免过度设计。
我写这篇文章,就是预留以后遇到这种问题,备个解决方案。
15年更新:
恰好进入的一家公司,图片存储占用的磁盘空间大。有几十个t,针对图片还要进行备份。同一张图片分为不同的尺寸。这样占据的空间比较麻烦。
基于存储成本考虑。使用动态生成图片尺寸的方式。
硬盘就保存原图。如果需要a尺寸,b尺寸。传递参数,当时生成一个返回,硬盘不保存多个尺寸的图。
使用了第三方的图片存储,没有自己搭建服务来生成尺寸。之所以使用第三方,因为他们有cdn加速服务,图片可以就近节点存储,用户访问可以就近节点进行访问图片。
目前的硬盘24t。快占满了。