图片访问实时处理的实现(nodejs和php)
我在访问时光网、网易云音乐等网站时,发现将它们页面中的一些图片URL修改一下就可以得到不同尺寸的图片,于是思考了其实现方案,我的思路是:URL Rewrite + 实时处理 + 缓存
,对用户请求的URL进行重写,然后利用图片处理类库对图片进行处理,接着缓存该尺寸图片并输出到浏览器。使用PHP和Node.js实现了一遍,基本达到了需要的效果。
1、Nginx+Node.js(express)实现
URL重写
这里Nginx主要是做一个URL重写和反向代理的功能,配置如下所示:
<code>location ~ /upload/{<br/> if ($request_uri ~* ^/upload/(.+)_(\d+)x(\d+)\.(jpg|png|gif)$) {<br/> set $src $1;<br/> set $w $2;<br/> set $h $3;<br/> set $t $4;<br/> rewrite . /resize?src=$src&w=$w&h=$h&type=$t break;<br/> }<br/> proxy_pass http://127.0.0.1:3000;<br/> }<br/> </code>
这里说明一下:Nginx监听本地的80端口,Node.js监听的是3000端口。当用户访问类似http://127.0.0.1/upload/147332819224704_400x300.jpg
的地址时,便会被代理到http://127.0.0.1:3000/resize?src=147332819224704&w=400&h=300&type=jpg
访问,看起来像是访问一张图片,其实不然。
图片实时处理
我们在Node.js中实时处理图片,进行缩放、模糊、水印等操作,之后将其缓存起来并输出到浏览器。Node.js自身API并不擅长图片的处理,我们可以借助第三方类库来实现,这里推荐GraphicsMagick或ImageMagick,使用它们之前先安装gm
模块:
<code>npm install gm --save<br/> </code>
接着便可以使用GraphicsMagick了,该模块的API可以参考GM模块API介绍。图片处理的实现如下:
<code>app.get('/resize',function(req,res){<br/> var src = req.query.src,<br/> width = req.query.w,<br/> height = req.query.h,<br/> type = req.query.type;<br/> var imgFile = uploadDir+src+'.'+type;<br/> var notFound = '不好意思,该图片不存在或已被删除!';<br/> var thumb = getThumbImg(src,width,height,type);<br/> if(isFileExists(imgFile)){<br/> if(isFileExists(thumb)){<br/> res.type(type).sendFile(__dirname+'/'+thumb);<br/> }else{<br/> imgResize(imgFile,thumb,width,height,type,res);<br/> }<br/> }else{<br/> res.status(404).send(notFound);<br/> }<br/> });<br/> function imgResize(f,th,w,h,t,r){<br/> var imgSize = sizeOf(f);<br/> if(!w||!h||w>=imgSize.width||h>=imgSize.height){<br/> r.type(t).sendFile(__dirname+'/'+f);<br/> }else{<br/> imageMagick(f)<br/> .resize(w,h,'!')<br/> .stream(function(err, stdout, stderr) {<br/> if (err) {<br/> console.log(err);<br/> res.end();<br/> }<br/> var ws = fs.createWriteStream(th);<br/> stdout.pipe(ws);<br/> r.type(t);<br/> stdout.pipe(r);<br/> });<br/> }<br/> }<br/> function isFileExists(filePath){<br/> var bool = !0;<br/> try{<br/> fs.accessSync(filePath,fs.constants.F_OK);<br/> }catch(err){<br/> bool = !1;<br/> }<br/> return bool;<br/> }<br/> </code>
如上代码所示,当用户访问http://127.0.0.1/upload/147332819224704_400x300.jpg
时,如果147332819224704这张图片存在,且400×300这个尺寸也存在,则直接输出这张图片,如不存在,则生成一张该尺寸的图片保存并输出到浏览器。如果提供的尺寸超出了图片的原始尺寸,则直接输出原图。我们不仅可以修改尺寸,也可以进行模糊、打水印等操作,这里就不多介绍了。
如果不用Nginx反向代理也是可以的,使用express的正则路由实现,如下所示:
<code>app.get(/^\/upload\/(.+)_(\d+)x(\d+)\.(jpg|png|gif)$/,function(req,res){<br/> var src = RegExp.$1,<br/> width = RegExp.$2,<br/> height = RegExp.$3,<br/> type = RegExp.$4;<br/> var imgFile = uploadDir+src+'.'+type;<br/> var notFound = '不好意思,该图片不存在或已被删除!';<br/> var thumb = getThumbImg(src,width,height,type);<br/> if(isFileExists(imgFile)){<br/> if(isFileExists(thumb)){<br/> res.type(type).sendFile(__dirname+'/'+thumb);<br/> }else{<br/> imgResize(imgFile,thumb,width,height,type,res);<br/> }<br/> }else{<br/> res.status(404).send(notFound);<br/> }<br/> });<br/> </code>
2、Apache+PHP实现
首先得搭建windows下php开发环境,我本人用的是apache2+php5.6,具体的搭建步骤网上一大堆,便不再累述。
开启apache rewrite功能
首先我们得开启Apache rewrite模块功能,去掉配置文件http.conf中LoadModule rewrite_module modules/mod_rewrite.so前面的注释,然后设置Directory块下AllowOverride All,可能有多处,接着重启Apache服务。
配置.htaccess文件
在DocumentRoot目录下,新建.htaccess
文件,如果创建不了,可以先创建一个文本,然后另存为,在弹出的对话框文件名处填写”.htaccess”即可。之后,编写URL重写规则,如下所示:
<code>RewriteEngine on<br/> RewriteCond %{REQUEST_FILENAME} !-f<br/> RewriteRule ^upload/(.+)_([0-9]+)x([0-9]+)\.(jpg|png|gif)$ resize.php?src=$1&w=$2&h=$3&type=$4 [L]<br/> </code>
将类似http://127.0.0.1/upload/147332819224704_400x300.jpg
地址重写为http://127.0.0.1/resize.php/src=147332819224704&w=400&h=300&type=jpg
。
功能实现
接下来便是功能的实现,逻辑和nodejs版逻辑一致,代码如下:
<code>function getThumbImg($src,$w,$h,$type)<br/> {<br/> global $thumbs;<br/> return $_SERVER['DOCUMENT_ROOT'].$thumbs.$src.'_'.$w.'_'.$h.'.'.$type;<br/> }<br/> function imgResize($f,$th,$w,$h,$t)<br/> {<br/> $imagick = new Imagick();<br/> $imagick->readImage($_SERVER['DOCUMENT_ROOT'].'\\'.$f);<br/> $width = $imagick->getImageWidth();<br/> $height = $imagick->getImageHeight();<br/> if(!$w||!$h||$w>=$width||$h>=$height){<br/> header('Content-Type:image/'.$t);<br/> echo file_get_contents($_SERVER['DOCUMENT_ROOT'].'\\'.$f);<br/> }else{<br/> $imagick->stripImage();<br/> $imagick->cropThumbnailImage($w, $h);<br/> $imagick->writeImage($th);<br/> header('Content-Type:image/'.$t);<br/> echo $imagick->getImageBlob();<br/> $imagick->clear();<br/> $imagick->destroy();<br/> }<br/> } $uploadDir = "uploads/images/";<br/> $thumbs = "uploads/thumbs/";<br/> $src = $_GET['src'];<br/> $width = $_GET['w'];<br/> $height = $_GET['h'];<br/> $type = $_GET['type'];<br/> $imgFile = $uploadDir.$src.'.'.$type;<br/> $notFound = '不好意思,该图片不存在或已被删除!';<br/> $thumb = getThumbImg($src,$width,$height,$type);<br/> if(file_exists($imgFile)){<br/> if(file_exists($thumb)){<br/> header('Content-Type:image/'.$type);<br/> echo file_get_contents($thumb);<br/> }else{<br/> imgResize($imgFile,$thumb,$width,$height,$type);<br/> }<br/> }else{<br/> header("HTTP/1.0 404 Not Found");<br/> header("status: 404");<br/> echo $notFound;<br/> }<br/> </code>