概述
每一個做過WEB程序開發的程序員(比如,博客、電商),應該都有這樣的體驗,有時候我們需要在不同的頁面顯示不同尺寸的圖片,比如,首頁、商品列表頁、詳情頁,如下:
首頁
列表頁
詳情頁
以上的需求,每天都在發生,雖然可以通過css來控制顯示大小。但是如果圖片過大,會造成加載的延遲,影響網站整體性能。因此,我們需要一個服務器來幫助我們進行圖片的裁剪。傳統做法是,上傳原圖像,然后根據提前規劃好的尺寸進行圖片的裁剪,然后保存各種尺寸的縮略圖,每種尺寸的縮略圖都在磁盤上有一個文件及文件名,下次訪問時通過圖片名稱訪問不同的縮略圖,當有新的尺寸需求時,修改代碼,多生成一個尺寸的縮略圖,如下類似的代碼,假設使用一個函數來生成縮略圖:
cutImg(100,100); //裁剪為100*100的縮略圖 cutImg(100,200); //裁剪為100*200的縮略圖 /* 這時添加一個新需求,500*500 像素的縮略圖 */ cutImg(500,500); //這里添加一條代碼生成一個新需求
這種方式不夠優雅,于是我們想想,有沒有辦法采用另一種方式,比如,我只上傳一張圖片,在使用時,通過需求可以顯示任務尺寸的縮略圖,而且不用預先規劃,如:
這是一張原圖:
圖片地址:http://qiniu.imecho.cn/apic15589.png
1、裁剪 100 * 100
如果這時根據UI的布局需求,需要一張:100px * 100px 的圖片
這時我發送一個請求:http://qiniu.imecho.cn/apic15589.png?imageView2/1/w/100/h/100
2、裁剪 200 * 50
如果有一個頁面還是顯示同一張圖片,但這時要求圖片大小是: 200px * 50px 的圖片
這時我們發送一個請求:http://qiniu.imecho.cn/apic15589.png?imageView2/1/w/200/h/50
發送請求:http://qiniu.imecho.cn/apic15589.png?imageView2/0/w/200/h/50
3、圓角圖片
如果,另外一個頁面,我需要一個圓角圖片 ? 。。。難道只能使用CSS嗎?當然不是,請看下面:
發送請求:http://qiniu.imecho.cn/apic15589.png?roundPic/radius/30
4、加水印
如果,我要加一個水印呢:傳統方法,得在代碼里添加: 添加水印的代碼,比如:
addWater('原圖路徑','水印圖路徑',$top,$left); // $top 距原圖上邊的距離,$left 距原圖左邊的距離
而且水印是固定的,一但生成水印,需要修改水印,需要重修改代碼,同時,再次執行代碼,那能不能通過請求來來動態生成水印呢?肯定是可以的,如:
請求:http://qiniu.imecho.cn/apic15589.png?watermark/1/image/aHR0cHM6Ly93d3cuYmFpZHUuY29tL2ltZy9iZF9sb2dvMS5wbmc=/dissolve/100/dx/50/dy/200
5、壓縮圖片
如果圖片文件太大,網絡傳輸時間太長,想要壓縮:如下:
壓縮前
壓縮后
還有更多的需求,比如圖片旋轉,圖片調色,圖片格式轉換等,是否可以通過這種動態生成圖片的方式實現呢,而且完全不用修改原代碼,直接通過請求參數實現
接下來,分析下php如何才能實現以上功能
總體架構
1、 后臺上傳圖片,到文件服務器【不做任務處理】
2、 瀏覽器發送請求
3、 服務器解析請求,并選擇指定的處理接口【壓縮、裁剪、圓角、加水印,其它】
4、 根據請求加載原圖片,并根據選擇的接口生成最終圖片
5、 返回給瀏覽器
實現步驟
1、 環境介紹
基于PHPstudy集成開發環境
域名:
項目目錄: E:projectphpimage
開啟 rewrite 重定向,優化URL
項目目錄結構:
2、圖片上傳
使用百度的 Web Uploader 上傳圖片即可,具體實現可以參考官網:
http://fex.baidu.com/webuploader/
圖片上傳不是本文分析的重點, 文件上傳功能已經實現
前端頁面:
后端可以使用百度提供的,也可以自己實現。
3、 Rewrite 重定向
下圖的案例可以看到,我們訪問圖片是能過網址加圖片名稱的方式
它是通過 Apache的重定向實現的,".htaccess" 這個文件就是配置文件,代碼如下
<IfModule mod_rewrite.c> RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ img.php </IfModule>
4、 入口文件實現
所有的圖片訪問都是通過 "img.php" 文件來實現的,那么它是如何工作的..
<?php require_once "function.php"; require_once "init.php";
"function.php" 文件
<?php function json($code=0,$msg=''){ $arr = ['code'=>$code, 'msg'=>$msg]; exit(json_encode($arr,JSON_UNESCAPED_UNICODE)); }
"init.php"文件
<?php define('ROOT_PATH', dirname(__FILE__)); //系統根目錄 define('DS',DIRECTORY_SEPARATOR); //目錄分隔符 define('IMAGE_PATH',ROOT_PATH . DS . 'img' . DS); //圖片路徑 define('PLUGIN_PATH',ROOT_PATH . DS . 'plugin' .DS); //插件路徑 define('IMAGE_CACHE_PATH',ROOT_PATH . DS . 'img_cache' . DS); //處理過的圖片緩存路徑 ? include_once PLUGIN_PATH . 'plugin.php'; //加載插件 ? //get請求參數 $query_string = isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:''; ? //圖片處理,以插件的形式處理圖片,每個插件都是對 $im 進行處理 if($query_string != ''){ include_once "route.php"; } else{ //輸出圖片,不做任何處理 $plugin = new plugin(); header('Content-type:'.$plugin->mime); //輸出類型 echo file_get_contents($plugin->img_file); exit; }
5、 路由的實現
這里的路由很簡單,無非就是通過請求?號后面的第一個參數,然后根據參數名引入對應的插件文件,然后實現化插件,代碼:
<?php //取得插件名稱 $plugin_name = $query_string; if(strpos($query_string,'/') !== false){ $plugin_name = substr($query_string,0,strpos($query_string,'/')); } //加載插件 $plugin_file = PLUGIN_PATH . $plugin_name . '.php'; //插件文件 if(!file_exists($plugin_file)){ json(4,'參數不正確'); }else{ include_once $plugin_file; new $plugin_name($query_string); //實例化插件 }
6、壓縮圖片
壓縮圖片是通過 imageslim 這個插件來實現的, 文件名 "plugin/imageslim.php"
代碼:
<?php class imageslim extends plugin{ public function __construct(){ parent::__construct(); ? $create_fun = $this->create_fun; //打開圖片函數名 $out_fun = $this->out_fun; //輸出圖片函數名 ? $im = $create_fun($this->img_file); //創建圖片對象 if(!$im){ json(3,'打開圖像失敗'); } ? header('Content-type:'.$this->mime); //輸出類型 $out_fun($im,null,50); //輸出圖片 imagedestroy($im); //銷毀圖片 } }
可以看到,實現原理很簡單, imageslim 繼承 plugin 插件,
plugin.php 插件實現一此成員變量的定義,及一些初始化操作
比如,文件名、文件類型、文件大小,代碼如下:
<?php class plugin { public $img_name; public $img_file; public $img_mime; public $mime; public $extension; public $create_fun; public $out_fun; public $src_w; //原始圖片的寬 public $src_h; //原始圖片的高 ? public function __construct() { //文件檢查 $this->img_name = trim($_SERVER['REDIRECT_URL'],'/'); $this->img_file = IMAGE_PATH . $this->img_name; if(!file_exists($this->img_file)){ json(1,'文件不存在'); } //圖片類型檢查 $this->img_mime = ['image/gif','image/png','image/jpeg','image/bmp','image/webp','image/x-icon','image/tiff']; //圖片mime $this->mime = mime_content_type($this->img_file); // 返回 mime 類型 if(!in_array($this->mime,$this->img_mime)){ json(2,'不是一張有效圖片'); } ? $this->extension = pathinfo($this->img_file,PATHINFO_EXTENSION); //獲取圖片后綴 $mime_end = substr($this->mime,6); $this->create_fun = 'imagecreatefrom'.$mime_end; //打開圖片函數名 $this->out_fun = 'image'.$mime_end; //輸出圖片函數名 ? //原始圖片尺寸 $image_info = getimagesize($this->img_file); $this->src_w = $image_info[0]; $this->src_h = $image_info[1]; } }
只有兩個關鍵函數,
第一個函數: Imgecreatefromjpeg 或 imagcreatefrompng 主要是根據圖片的類型是什么來調用對應的打開函數
第二個函數: imagejpeg 或 imagepng 也是根據圖片類型來輸出對應的函數
7、裁剪圖片
這里是才是裁剪圖片主要的功能,我們需要根據各種需求進行裁剪,工作中我們都會怎樣進行裁剪呢?
第一種: 等比縮放到我們指定的寬,或指定的高
請求接口:http://www.image.com/26.jpg?imageview/0/w/300/h/200
限定縮略圖的長邊最多為<LongEdge>,短邊最多為<ShortEdge>,進行等比縮放,不裁剪。
如果只指定 w 參數則表示限定長邊(短邊自適應),只指定 h 參數則表示限定短邊(長邊自適應)
關鍵函數: imagecopyresized
第二種:等比縮放,居中裁剪
請求接口:http://www.image.com/26.jpg?imageView/1/w/100/h/140
限定縮略圖的寬最少為<Width>,高最少為<Height>,進行等比縮放,居中裁剪。
轉后的縮略圖通常恰好是 <Width>x<Height> 的大?。ㄓ幸粋€邊縮放的時候會因為超出矩形框而被裁剪掉多余部分)。
如果只指定 w 參數或只指定 h 參數,代表限定為長寬相等的正方圖
第三種:等比縮放,不裁剪
請求接口http://www.image.com/26.jpg?imageView/2/w/100/h/140
限定縮略圖的寬最多為<Width>,高最多為<Height>,進行等比縮放,不裁剪。如果只指定 w 參數則表示限定寬(長自適應),
只指定 h 參數則表示限定長(寬自適應)。它和模式0類似,區別只是限定寬和高,不是限定長邊和短邊。
從應用場景來說,模式0適合移動設備上做縮略圖,模式2適合PC上做縮略圖
第四種:限定寬高最小值,不裁剪
請求接口http://www.image.com/26.jpg?imageView/3/w/100/h/140
限定縮略圖的寬最少為<Width>,高最少為<Height>,進行等比縮放,不裁剪。如果只指定 w 參數或只指定 h 參數,代表長寬限定為同樣的值。你可以理解為模式1是模式3的結果再做居中裁剪得到的
總結
以上就是基本的圖片裁剪,當然還有一些裁剪模式,我們沒有實現 ,比如固定大小,等比縮放,并居中顯示,如下
再者還有,添加水印等功能,都屬于圖片裁剪,由于篇幅有限,其它功能自行研究一一實現
IT技術研習社,專注互聯網技術研究與分享,喜歡的朋友可以點擊【關注】;把經驗傳遞給有夢想的人;