nodejs和php實(shí)現(xiàn)圖片訪問(wèn)實(shí)時(shí)處理
我在訪問(wèn)時(shí)光網(wǎng)、網(wǎng)易云音樂(lè)等網(wǎng)站時(shí),發(fā)現(xiàn)將它們頁(yè)面中的一些圖片URL修改一下就可以得到不同尺寸的圖片,于是思考了其實(shí)現(xiàn)方案,我的思路是:URL Rewrite + 實(shí)時(shí)處理 + 緩存,對(duì)用戶(hù)請(qǐng)求的URL進(jìn)行重寫(xiě),然后利用圖片處理類(lèi)庫(kù)對(duì)圖片進(jìn)行處理,接著緩存該尺寸圖片并輸出到瀏覽器。使用PHP和Node.js實(shí)現(xiàn)了一遍,基本達(dá)到了需要的效果。
1、Nginx+Node.js(express)實(shí)現(xiàn)
URL重寫(xiě) 這里Nginx主要是做一個(gè)URL重寫(xiě)和反向代理的功能,配置如下所示:
- location ~ /upload/{
- if ($request_uri ~* ^/upload/(.+)_(\d+)x(\d+)\.(jpg|png|gif)$) {
- set $src $1;
- set $w $2;
- set $h $3;
- set $t $4;
- rewrite . /resize?src=$src&w=$w&h=$h&type=$t break;
- }
- proxy_pass http://127.0.0.1:3000;
- }
這里說(shuō)明一下:Nginx監(jiān)聽(tīng)本地的80端口,Node.js監(jiān)聽(tīng)的是3000端口。當(dāng)用戶(hù)訪問(wèn)類(lèi)似http://127.0.0.1/upload/147332819224704_400x300.jpg的地址時(shí),便會(huì)被代理到http://127.0.0.1:3000/resize?src=147332819224704&w=400&h=300&type=jpg訪問(wèn),看起來(lái)像是訪問(wèn)一張圖片,其實(shí)不然。 圖片實(shí)時(shí)處理 我們?cè)贜ode.js中實(shí)時(shí)處理圖片,進(jìn)行縮放、模糊、水印等操作,之后將其緩存起來(lái)并輸出到瀏覽器。Node.js自身API并不擅長(zhǎng)圖片的處理,我們可以借助第三方類(lèi)庫(kù)來(lái)實(shí)現(xiàn),這里推薦GraphicsMagick或ImageMagick,使用它們之前先安裝gm模塊: npm install gm --save 接著便可以使用GraphicsMagick了,該模塊的API可以參考GM模塊API介紹。圖片處理的實(shí)現(xiàn)如下:
- app.get('/resize',function(req,res){
- var src = req.query.src,
- width = req.query.w,
- height = req.query.h,
- type = req.query.type;
- var imgFile = uploadDir+src+'.'+type;
- var notFound = '不好意思,該圖片不存在或已被刪除!';
- var thumb = getThumbImg(src,width,height,type);
- if(isFileExists(imgFile)){
- if(isFileExists(thumb)){
- res.type(type).sendFile(__dirname+'/'+thumb);
- }else{
- imgResize(imgFile,thumb,width,height,type,res);
- }
- }else{
- res.status(404).send(notFound);
- }
- });
- function imgResize(f,th,w,h,t,r){
- var imgSize = sizeOf(f);
- if(!w||!h||w>=imgSize.width||h>=imgSize.height){
- r.type(t).sendFile(__dirname+'/'+f);
- }else{
- imageMagick(f)
- .resize(w,h,'!')
- .stream(function(err, stdout, stderr) {
- if (err) {
- console.log(err);
- res.end();
- }
- var ws = fs.createWriteStream(th);
- stdout.pipe(ws);
- r.type(t);
- stdout.pipe(r);
- });
- }
- }
- function isFileExists(filePath){
- var bool = !0;
- try{
- fs.accessSync(filePath,fs.constants.F_OK);
- }catch(err){
- bool = !1;
- }
- return bool;
- }
如上代碼所示,當(dāng)用戶(hù)訪問(wèn)http://127.0.0.1/upload/147332819224704_400x300.jpg時(shí),如果147332819224704這張圖片存在,且400x300這個(gè)尺寸也存在,則直接輸出這張圖片,如不存在,則生成一張?jiān)摮叽绲膱D片保存并輸出到瀏覽器。如果提供的尺寸超出了圖片的原始尺寸,則直接輸出原圖。我們不僅可以修改尺寸,也可以進(jìn)行模糊、打水印等操作,這里就不多介紹了。 如果不用Nginx反向代理也是可以的,使用express的正則路由實(shí)現(xiàn),如下所示:
- app.get(/^\/upload\/(.+)_(\d+)x(\d+)\.(jpg|png|gif)$/,function(req,res){
- var src = RegExp.$1,
- width = RegExp.$2,
- height = RegExp.$3,
- type = RegExp.$4;
- var imgFile = uploadDir+src+'.'+type;
- var notFound = '不好意思,該圖片不存在或已被刪除!';
- var thumb = getThumbImg(src,width,height,type);
- if(isFileExists(imgFile)){
- if(isFileExists(thumb)){
- res.type(type).sendFile(__dirname+'/'+thumb);
- }else{
- imgResize(imgFile,thumb,width,height,type,res);
- }
- }else{
- res.status(404).send(notFound);
- }
- });
2、Apache+PHP實(shí)現(xiàn) 首先得搭建windows下php開(kāi)發(fā)環(huán)境,我本人用的是apache2+php5.6,具體的搭建步驟網(wǎng)上一大堆,便不再累述。 開(kāi)啟apache rewrite功能 首先我們得開(kāi)啟Apache rewrite模塊功能,去掉配置文件http.conf中LoadModule rewritemodule modules/modrewrite.so前面的注釋?zhuān)缓笤O(shè)置Directory塊下AllowOverride All,可能有多處,接著重啟Apache服務(wù)。 配置.htaccess文件 在DocumentRoot目錄下,新建.htaccess文件,如果創(chuàng)建不了,可以先創(chuàng)建一個(gè)文本,然后另存為,在彈出的對(duì)話(huà)框文件名處填寫(xiě)".htaccess"即可。之后,編寫(xiě)URL重寫(xiě)規(guī)則,如下所示:
- RewriteEngine on
- RewriteCond %{REQUEST_FILENAME} !-f
- RewriteRule ^upload/(.+)_([0-9]+)x([0-9]+)\.(jpg|png|gif)$ resize.php?src=$1&w=$2&h=$3&type=$4 [L]
將類(lèi)似http://127.0.0.1/upload/147332819224704_400x300.jpg地址重寫(xiě)為http://127.0.0.1/resize.php/src=147332819224704&w=400&h=300&type=jpg。 功能實(shí)現(xiàn) 接下來(lái)便是功能的實(shí)現(xiàn),邏輯和nodejs版邏輯一致,代碼如下:
- function getThumbImg($src,$w,$h,$type)
- {
- global $thumbs;
- return $_SERVER['DOCUMENT_ROOT'].$thumbs.$src.'_'.$w.'_'.$h.'.'.$type;
- }
- function imgResize($f,$th,$w,$h,$t)
- {
- $imagick = new Imagick();
- $imagick->readImage($_SERVER['DOCUMENT_ROOT'].'\\'.$f);
- $width = $imagick->getImageWidth();
- $height = $imagick->getImageHeight();
- if(!$w||!$h||$w>=$width||$h>=$height){
- header('Content-Type:image/'.$t);
- echo file_get_contents($_SERVER['DOCUMENT_ROOT'].'\\'.$f);
- }else{
- $imagick->stripImage();
- $imagick->cropThumbnailImage($w, $h);
- $imagick->writeImage($th);
- header('Content-Type:image/'.$t);
- echo $imagick->getImageBlob();
- $imagick->clear();
- $imagick->destroy();
- }
- }
- $uploadDir = "uploads/images/";
- $thumbs = "uploads/thumbs/";
- $src = $_GET['src'];
- $width = $_GET['w'];
- $height = $_GET['h'];
- $type = $_GET['type'];
- $imgFile = $uploadDir.$src.'.'.$type;
- $notFound = '不好意思,該圖片不存在或已被刪除!';
- $thumb = getThumbImg($src,$width,$height,$type);
- if(file_exists($imgFile)){
- if(file_exists($thumb)){
- header('Content-Type:image/'.$type);
- echo file_get_contents($thumb);
- }else{
- imgResize($imgFile,$thumb,$width,$height,$type);
- }
- }else{
- header("HTTP/1.0 404 Not Found");
- header("status: 404");
- echo $notFound;
- }
至此,圖片訪問(wèn)實(shí)時(shí)處理也就完成了。其實(shí)大部分圖片服務(wù)器都需要這樣的功能,而不是事先生成好幾套尺寸的圖片。