### 獲取圖片微縮圖有兩種方法
1. 生成微縮圖,返回微縮圖地址給src使用,這種方法一般直接在模板中調用 src="{:get_thum_images()}"
2. 獲取顯示微縮圖的地址,這個地址并不是微縮圖的真是地址,而是生成程序的地址,此地址沒訪問就不會執行,因此在模板中使用不會阻塞執行,因為它僅僅是生成一個地址而已,瀏覽器渲染DOM時再請求src中的這個地址,此時才會進入到圖片微縮圖生成流程,當然也有緩存,如果有緩存直接輸出已生成的圖片
> 顯然這個函數使用的是第一種方案,因此會出現生成微縮圖時間過長,第二種方法不會,因為程序每次只生成一次微縮圖,相當于分散了生成壓力,第二種方法還有一個好處就是現在前端很多都是懶加載的,所以沒必要生成模板時生成所有可能不需要的微縮圖,所以一些情況下使用第二種方式會大大提高效率減少壓力,現在很多大型汪涵也都是這么做的,不過第二種方案也有一個弊端,那就是性能問題,因為每次都需要將圖片讀到內存在輸出到瀏覽器(第一種方案生成后不走php,直接走Nginx的靜態服務了),很多儲存平臺都是使用專門的服務器做這項服務,如果單純的使用php腳本承擔這個任務的話可能有點壓力。
## 探討
**區別**
第一種方案和第二種方案的區別在于第一種方案是生成微縮圖文件后再輸出微縮圖的真實地址,然后src獲取圖片流的時候走的是Nginx的靜態服務;第二種方案是先得到能夠輸出圖片流的地址給src,等真正請求微縮圖時再請求此地址才生成微縮圖,并直接將微縮圖以圖片流的形式輸出,而不是輸出圖片地址,這種方案相當于是使用了中轉/代理的機制,只要它們最終能夠在src上輸出圖片流,圖片就能正常顯示。
**圖片流**
src="?"要想顯示圖片,那么這個地址必須只想一個圖片輸出流,回到上面的問題,可以知道第一種方案圖片流是由Nginx的靜態服務提供的,第二種是微縮程序用php輸出的,只要最終src得到它要的圖片流,那么就能顯示圖片,而不管這個圖片流是由誰提供的。
**緩存**
不管何種方式,為了效率,都使用了緩存,比如第一種方式第一次生成圖片可能慢點,第二次生成相同規格的圖片就會直接返回已經存在的圖片地址;第二種方式還是同樣的,如果發現存在已經生成過的微縮圖,那么會直接讀取輸出,不會再次生成。
**使用場景**
1. 直接在模板中調用
2. 做接口,第一種方案返回的是微縮圖地址字符串;第二種情況有兩種方式做接口:(1)是直接輸出圖片流,(2)返回拼接的能夠輸出圖片流的地址。不管怎么樣這兩種方案放到src上面都能夠顯示圖片。
*(關于性能下面會討論)*
**性能**
從生成微縮圖程序本身來講,兩種方案性能是一樣的,因為用的是一樣的微縮圖生成程序,但是使用場景的不同,就會有性能差異。
生成微縮圖程序一般是模板中調用,由于第一種方式是直接生成,所以當量比較大(在模板中調用次數比較多),那么就會很慢(第一次慢,其后就有緩存了),而第二種方案,只是生成一個能夠輸出圖片流的地址,或者說只是簡單拼接出一個地址,并不會有生成微縮圖的動作,所以比較快,然后這個地址返回給src,等到真正去加載這個圖片時,才會訪問這個能夠輸出圖片流地址,此時才會真正生成微縮圖,所以這種情況下第一種方案性能高一些。
從另一個角度來說,第一種方式在模板中使用,(在一個請求生命周期/php生命周期)被多次連續調用,就需要連續執行微縮圖生成程序多次,所以慢,但是生成之后就會走Nginx的靜態服務,效率就會高;而第二種方案,只是在真正訪問圖片時才會去請求能輸出圖片流的地址,一張圖片一個請求,一個請求生成一個微縮圖,也就是說將壓力分散了,但是分散的每次請求都是php處理圖片輸出,性能沒有Nginx高。一個集中的負載,一個多次的單一的請求,哪個隊用戶體驗更好,哪個性能更高,哪個對服務器資源使用更合理,這個看實際情況了,沒有最好的,只有最合適的,換句話說,最合適的就是最好的。
如果做接口的話,一個是生成微縮圖再返回微縮圖文件地址,一個是直接生成微縮圖,直接輸出,或者返回拼接的能夠輸出圖片流的地址,將生成負載消耗留給下次的圖片請求。
**結論**
經過詳細分析兩種方案各自的有點和缺點,所以,根據實際情況,考慮采用什么方案才是最好的。
- 如果模板每次調用微縮圖次數不多,那么采用第一種方案也是可以的,這樣下次就會快了。
- 如果第一種方案有時候太慢了,影響用戶體驗,并且你希望有更靈活的方式,那么可以采用第二種方案。
- 而如果第二種方案會讓你的服務器頂不住,或者感覺效率也并不高的話,那么就要考慮其他方法了,或者升級第二種方案的輸出圖片程序,像那些云儲存平臺一樣,使用專門的服務器,更適合的語言去做這樣的事,不斷優化,找出影響性能的問題去解決,去突破瓶頸,還是一樣,沒有最好的設計,只有最合適的設計。
>[info] 如果擔心第一種方式有性能問題,第二種方式會增加服務器多的負擔,不想讓php輸出靜態文件,那么有一種同時兼顧二者的方案,第三個函數:判斷如果當前圖片微縮圖存在就直接返回,不存在就返回調用第二種方案并返回。這樣就同時兼顧兩種方案了。
* * * * *
### 其他
http://iconfont.alicdn.com/t/1479434967283.png@100h_100w.jpg
https://fuss10.elemecdn.com/b/d2/ba7e66cfd1f3f6c431595b887675djpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/85
https://dn-coding-net-production-static.qbox.me/fc0ccc44-a5a6-4c18-89be-df5c23ecc143.png?imageMogr2/auto-orient/format/png/crop/!256x256a0a0
比如這樣的圖片可能是專門的服務器做的
### 參考代碼:
~~~php
/**
* 輸出微縮圖(獲取微縮圖輸出地址)(性能相對高些,因為真實訪問時畢竟是要輸出圖片流的)
* @param string $src 圖片url 從數據庫直接讀出來的 Uploads/Pic/2016-03-04/6sdfb345bds43f.png
* @return int $width 寬
* @return int $height 高
* @return string $format 格式
* @return int $type 類型
* @return int $q 質量
* @return int $s 是否生成緩存
* @return int $o 是否使用緩存
* @return string 圖片輸出地址 Home/Pic/thumb/img/Uploads/Pic/2015-12-07/566523b7062dc.jpg/w/150/h/150/f/png/t/3/q/50/s/2/o/2
*/
function thumb($src, $width = 60, $height = 60, $format = 'jpg', $type = 2, $q = 80, $s = 1, $o = 1) {
$array = array(
'w' => $width ? : 0,
'h' => $height ? : 0,
'f' => $format,
't' => $type,
'q' => $q,
's' => $s,
'o' => $o
);
if ('jpg' == $format) unset($array['f']);
if (2 == $type) unset($array['t']);
if (80 == $q) unset($array['q']);
if (1 == $s) unset($array['s']);
if (1 == $o) unset($array['o']);
C('URL_CASE_INSENSITIVE', false);
return U('Home/Pic/thumb/img/' . $src, $array, false);
}
/**
* 獲取微縮圖地址src(性能相對低一些,輸出的是真實圖片地址)
* @param string $src 圖片url 從數據庫直接讀出來的 Uploads/Pic/2016-03-04/6sdfb345bds43f.png
* @return int $width 寬
* @return int $height 高
* @return string $format 格式
* @return int $type 類型
* @return int $q 質量
* @return string 圖片輸出地址 /_thumb/Uploads/Pic/2016-03-21/56eee5d54bc61_20-20-jpg-2-80.jpg
*/
function get_thumb_src($src, $width = 60, $height = 60, $format = 'jpg', $type = 2, $q = 80) {
$width = $width ? : 0;
$height = $height ? : 0;
return A('Home/Pic')->get_thumb_src($src, $width, $height, $format, $type, $q);
}
~~~
PicController.class.php
~~~php
<?php
namespace Home\Controller;
use Core\Image;
/**
* 圖片控制器
* Update time:2016-6-7 09:21:41
*/
class PicController {
/**
* 輸出微縮圖 home/pic/thumb/img/uploads/pic/2015-12-07/566523b7062dc.jpg/w/150/h/150/f/png/t/3/q/50/s/2/o/2
* @param string $src 圖像地址
* @param string $width 寬
* @param string $height 高
* @param string $format 輸出格式 默認jpg(貌似只有jpg支持清晰度調節)
* @param string $type 微縮方式 默認2 縮放后填充類型
* @param string $q 清晰度調節 默認80
* @param string $s 是否緩存微縮圖 默認1緩存
* @param string $o 是否使用微縮圖緩存 默認1可以使用
* @return 圖片流
*/
public function thumb() {
C('SHOW_PAGE_TRACE', false);
if (!preg_match("#/img/(.*)/w/(\d+)/h/(\d+)#is", __SELF__, $matches)) return;
preg_match("#/f/(\w+)#is", __SELF__, $a);
preg_match("#/t/(\d+)#is", __SELF__, $b);
preg_match("#/q/(\d+)#is", __SELF__, $c);
preg_match("#/s/(\d+)#is", __SELF__, $d);
preg_match("#/o/(\d+)#is", __SELF__, $e);
$src = $matches[1];
$width = $matches[2];
$height = $matches[3];
$format = isset($a[1]) ? $a[1] : 'jpg';
$type = isset($b[1]) ? $b[1] : 2;
$q = isset($c[1]) ? $c[1] : 80;
$s = isset($d[1]) ? $d[1] : 1;
$o = isset($e[1]) ? $e[1] : 1;
$img = $this->getThumbFile($src, $width, $height, $format, $type, $q);
if ($o && $this->isFile($src, $width, $height, $format, $type, $q)) {
$fileExt = pathinfo($img, PATHINFO_EXTENSION);
$mtime = filemtime($img);
$gmdate_mod = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
header('Last-Modified: ' . $gmdate_mod);
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (60*60*24*30)) . ' GMT');
header('Content-type: image/' . $fileExt);
header('Content-Length: ' . filesize($img));
readfile($img);
return;
}
if (!file_exists(PATH . $src)) {
return '';
}
$image = new Image();
$image->open(PATH . $src);
$width = $width == 0 ? $image->width() : $width;
$height = $height == 0 ? $image->height() : $height;
if ($s) {
$dir = dirname($img);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$image->thumb($width, $height, $type, $q)->save($img, $format, $q)->ss($format, $q);
} else {
$image->thumb($width, $height, $type, $q)->ss($format, $q);
}
}
// 獲取微縮圖url
public function get_thumb_src($src, $width, $height, $format, $type, $q) {
if (!file_exists(PATH . $src)) {
return '';
}
if(!$this->isFile($src, $width, $height, $format, $type, $q)) {
$this->createThumb($src, $width, $height, $format, $type, $q);
}
return str_replace(PATH, __ROOT__ . '/', $this->getThumbFile($src, $width, $height, $format, $type, $q));
}
// 計算微縮圖文件地址
private function getThumbFile($src, $width, $height, $format, $type, $q) {
$fileExt = pathinfo($src, PATHINFO_EXTENSION);
$src = str_replace('.' . $fileExt, '', $src);
return PATH . '_thumb/' . $src . '_' . $width . '-' . $height . '-' . $format . '-' . $type . '-' . $q . '.' . $fileExt;
}
// 創建微縮圖
private function createThumb($src, $width, $height, $format, $type, $q) {
$img = $this->getThumbFile($src, $width, $height, $format, $type, $q);
$image = new Image();
$image->open(PATH . $src);
$dir = dirname($img);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$image->thumb($width, $height, $type)->save($img, $format, $q);
}
// 檢測微縮圖是否存在
private function isFile($src, $width, $height, $format, $type, $q) {
$img = $this->getThumbFile($src, $width, $height, $format, $type, $q);
return file_exists($img) ? true : false;
}
}
~~~
還有這個
~~~php
function get_thum_images($src, $width, $height, $mode = 4)
{
// 獲取圖片微縮圖有兩種方法
// 1:生成微縮圖,返回微縮圖地址給src使用,這種方法一般直接在模板中調用 src="{:get_thum_images()}"
// 2:獲取顯示微縮圖的地址,這個地址并不是微縮圖的真是地址,而是生成程序的地址,此地址沒訪問就不會執行,因此在模板中使用不會阻塞執行,因為它僅僅是生成一個地址而已,瀏覽器渲染DOM時再請求src中的這個地址,此時才會進入到圖片微縮圖生成流程,當然也有緩存,如果有緩存直接輸出已生成的圖片
// 1,2區別在于1是生成微縮圖在輸出微縮圖真實地址,2是得到微縮程序地址給src,等真正請求微縮圖時在生成微縮圖,并直接輸出圖片流,而不是輸出圖片地址
// 顯然這個函數使用的是第一種方案,因此會出現生成微縮圖時間過長,第二種方法不會,因為圖片每次只生成一次微縮圖,相當于分散了生成壓力,第二種方法還有一個好處就是現在前端很多都是懶加載的,所以沒必要生成模板時生成所有可能不需要的微縮圖,所以一些情況下使用第二種方式會大大提高效率減少壓力,現在很多大型汪涵也都是這么做的,不過第二種方案也有一個弊端,那就是性能問題,因為每次都需要將圖片讀到內存在輸出到瀏覽器(第一種方案生成后不走php,直接走Nginx的靜態服務了),很多儲存平臺都是使用專門的服務器做這項服務,如果單純的使用php腳本承擔這個任務的話可能有點壓力。
set_time_limit(0);
ini_set('memory_limit','-1');
if (!file_exists('.' . $src)) {
return '';
}
$fileExt = pathinfo($src, PATHINFO_EXTENSION);
$src = str_replace('.' . $fileExt, '', $src);
//判斷縮略圖是否存在
$path = "/Public/_thumb";
$thumb = "{$src}_{$width}_{$height}_{$mode}.{$fileExt}";
if (file_exists('.' . $path . $thumb)) {
return $path . $thumb;
}
$image = new \Think\Image();
$image->open('.' . $src . '.' . $fileExt);
// 圖像的路徑必須要自己創建,否則保存失敗
$dir = dirname('.' . $path . $thumb);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
//參考文章 http://www.mb5u.com/biancheng/php/php_84533.html 改動參考 http://www.thinkphp.cn/topic/13542.html
$a = $image->thumb($width, $height, $mode)->save('.' . $path . $thumb, null, 100); //按照原圖的比例生成一個最大為$width*$height的縮略圖并保存
// 生成失敗返回原圖
if (!file_exists('.' . $path . $thumb)) {
return $src . '.' . $fileExt;
}
return $path . $thumb;
}
~~~
* * * * *
### 擴展
哈哈,找到幾個開源的圖片壓縮解決方案
- [TinyPNG – API Reference](https://tinypng.com/developers/reference/php)
- [pngquant — lossy PNG compressor](https://pngquant.org/)
- [移動端高清、多屏適配方案 - Div.IO](http://div.io/topic/1092)
> 圖片服務器,圖片適配多種終端
- [用 imgproxy 自動縮放圖片](https://mp.weixin.qq.com/s/25Pt37Xe9_MdyJEX4Lm0eg)
- [原生 JS 實現最簡單的圖片懶加載](https://mp.weixin.qq.com/s/yP5nwH6hpPh1uPgRtSET8A)
- [\[譯\] 大型網站前端使用圖片格式的正確姿勢](https://mp.weixin.qq.com/s/7AI5QXlTG50k-qlIQSxcQA)
[nodejs和php實現圖片訪問實時處理](https://zhuanlan.zhihu.com/p/31426592)
[淺談Web圖像優化](http://mp.weixin.qq.com/s/kg0nOP878OsGjSE06qtF8g)
[george518/CoreImage: 裁剪核心圖片](https://github.com/george518/CoreImage)
[PHP漏洞|一張GIF圖片就能讓服務器宕機的PHP漏洞](https://mp.weixin.qq.com/s/y0SFNLj0O7PDLW4f5OI-Pg)
[閑談 Web 圖片服務器 | DBA Notes](http://dbanotes.net/web/web_image_server.html)
[Yupoo! 的網站技術架構 | DBA Notes](http://dbanotes.net/arch/yupoo_arch.html)
* * * * *
update time:2018-1-14 17:38:31
- 開始
- 公益
- 更好的使用看云
- 推薦書單
- 優秀資源整理
- 技術文章寫作規范
- SublimeText - 編碼利器
- PSR-0/PSR-4命名標準
- php的多進程實驗分析
- 高級PHP
- 進程
- 信號
- 事件
- IO模型
- 同步、異步
- socket
- Swoole
- PHP擴展
- Composer
- easyswoole
- php多線程
- 守護程序
- 文件鎖
- s-socket
- aphp
- 隊列&并發
- 隊列
- 講個故事
- 如何最大效率的問題
- 訪問式的web服務(一)
- 訪問式的web服務(二)
- 請求
- 瀏覽器訪問阻塞問題
- Swoole
- 你必須理解的計算機核心概念 - 碼農翻身
- CPU阿甘 - 碼農翻身
- 異步通知,那我要怎么通知你啊?
- 實時操作系統
- 深入實時 Linux
- Redis 實現隊列
- redis與隊列
- 定時-時鐘-阻塞
- 計算機的生命
- 多進程/多線程
- 進程通信
- 拜占庭將軍問題深入探討
- JAVA CAS原理深度分析
- 隊列的思考
- 走進并發的世界
- 鎖
- 事務筆記
- 并發問題帶來的后果
- 為什么說樂觀鎖是安全的
- 內存鎖與內存事務 - 劉小兵2014
- 加鎖還是不加鎖,這是一個問題 - 碼農翻身
- 編程世界的那把鎖 - 碼農翻身
- 如何保證萬無一失
- 傳統事務與柔性事務
- 大白話搞懂什么是同步/異步/阻塞/非阻塞
- redis實現鎖
- 淺談mysql事務
- PHP異常
- php錯誤
- 文件加載
- 路由與偽靜態
- URL模式之分析
- 字符串處理
- 正則表達式
- 數組合并與+
- 文件上傳
- 常用驗證與過濾
- 記錄
- 趣圖
- foreach需要注意的問題
- Discuz!筆記
- 程序設計思維
- 抽象與具體
- 配置
- 關于如何學習的思考
- 編程思維
- 談編程
- 如何安全的修改對象
- 臨時
- 臨時筆記
- 透過問題看本質
- 程序后門
- 邊界檢查
- session
- 安全
- 王垠
- 第三方數據接口
- 驗證碼問題
- 還是少不了虛擬機
- 程序員如何談戀愛
- 程序員為什么要一直改BUG,為什么不能一次性把代碼寫好?
- 碎碎念
- 算法
- 實用代碼
- 相對私密與絕對私密
- 學習目標
- 隨記
- 編程小知識
- foo
- 落盤
- URL編碼的思考
- 字符編碼
- Elasticsearch
- TCP-IP協議
- 碎碎念2
- Grafana
- EFK、ELK
- RPC
- 依賴注入
- 開發筆記
- 經緯度格式轉換
- php時區問題
- 解決本地開發時調用遠程AIP跨域問題
- 后期靜態綁定
- 談tp的跳轉提示頁面
- 無限分類問題
- 生成微縮圖
- MVC名詞
- MVC架構
- 也許模塊不是唯一的答案
- 哈希算法
- 開發后臺
- 軟件設計架構
- mysql表字段設計
- 上傳表如何設計
- 二開心得
- awesomes-tables
- 安全的代碼部署
- 微信開發筆記
- 賬戶授權相關
- 小程序獲取是否關注其公眾號
- 支付相關
- 提交訂單
- 微信支付筆記
- 支付接口筆記
- 支付中心開發
- 下單與支付
- 支付流程設計
- 訂單與支付設計
- 敏感操作驗證
- 排序設計
- 代碼的運行環境
- 搜索關鍵字的顯示處理
- 接口異步更新ip信息
- 圖片處理
- 項目搭建
- 閱讀文檔的新方式
- mysql_insert_id并發問題思考
- 行鎖注意事項
- 細節注意
- 如何處理用戶的輸入
- 不可見的字符
- 抽獎
- 時間處理
- 應用開發實戰
- python 學習記錄
- Scrapy 教程
- Playwright 教程
- stealth.min.js
- Selenium 教程
- requests 教程
- pyautogui 教程
- Flask 教程
- PyInstaller 教程
- 蜘蛛
- python 文檔相似度驗證
- thinkphp5.0數據庫與模型的研究
- workerman進程管理
- workerman網絡分析
- java學習記錄
- docker
- 筆記
- kubernetes
- Kubernetes
- PaddlePaddle
- composer
- oneinstack
- 人工智能 AI
- 京東
- pc_detailpage_wareBusiness
- doc
- 電商網站設計
- iwebshop
- 商品規格分析
- 商品屬性分析
- tpshop
- 商品規格分析
- 商品屬性分析
- 電商表設計
- 設計記錄
- 優惠券
- 生成唯一訂單號
- 購物車技術
- 分類與類型
- 微信登錄與綁定
- 京東到家庫存系統架構設計
- crmeb
- 命名規范
- Nginx https配置
- 關于人工智能
- 從人的思考方式到二叉樹
- 架構
- 今日有感
- 文章保存
- 安全背后: 瀏覽器是如何校驗證書的
- 避不開的分布式事務
- devops自動化運維、部署、測試的最后一公里 —— ApiFox 云時代的接口管理工具
- 找到自己今生要做的事
- 自動化生活
- 開源與漿果
- Apifox: API 接口自動化測試指南