### 获取图片微缩图有两种方法
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 接口自动化测试指南