原文:减少HTTP请求之合并图片详解(大型网站优化技术)

  一、相关知识讲解

  看过雅虎的前端优化35条建议,都知道优化前端是有多么重要。页面的加载速度直接影响到用户的体验。80%的终端用户响应时间都花在了前端上,其中大部分时间都在下载页面上的各种组件:图片,样式表,脚本,Flash等等。

  减少组件数必然能够减少页面提交的HTTP请求数。这是让页面更快的关键。减少页面组件数的一种方式是简化页面设计。但有没有一种方法可以在构建复杂的页面同时加快响应时间呢?嗯,确实有鱼和熊掌兼得的办法。

  这里我们就拿雅虎的第一条建议:尽量减少HTTP请求数里的减少图片请求数量 进行讲解。

  我们都知道,一个网站的一个页面可能有很多小图标,例如一些按钮、箭头等等。当加载html文档时,只要遇到有图片的,都会自动建立起HTTP请求下载,然后将图片下载到页面上,这些小图片可能也就是十几K大甚至1K都不到,假如我们的一个页面有一百个小图标,我们在加载页面时,就要发送100个HTTP请求,如果你的网站访问量很大并发量也很高,假如上百万访问量,那发起的请求就是千万级别了,服务器是有一定的压力的,并且一个用户的一个页面要发起那么多请求,是很耗时的。

  所以,我们优化的方案就是:将这些十几K、几K的小图标合并在一张图片里,然后用CSS的background-imagebackground-position属性来定位要显示的部分。

  、代码实现

  1、思路:

    将一个文件夹里的图标,自动生成在一张图片里面,同时自动生成对应的css文件,我们只要在HTML里的标签中添加相应的属性值就能显示图片了。

  2、实现过程:

 <?php
//自己定义一个根目录
define('ROOT', $_SERVER['DOCUMENT_ROOT'].'iconwww');
//这个是图片的目录
define('RES_BASE_URL', 'http://localhost:8080/iconwww/img'); /**
* 生成背景图的函数
*/
function generateIcon() {
//网站根目录
$webRoot = rtrim(ROOT, '/');
//背景图目录
$root = "$webRoot/img/bg";
//Php-SPL库中 的 目录文件遍历器
$iterator = new DirectoryIterator($root);
//开始遍历该背景图目录下的目录,我们是把想生成背景图的目录,放在bg目录中以各个模块的目录分类存放
foreach ($iterator as $file) {
//遇到目录遍历
if (!$file->isDot() && $file->isDir()) {
//取得文件名
$fileName = $file->getFilename();
generateIconCallback("$root/$fileName", "$webRoot/img/$fileName", "$webRoot/css/$fileName.css");
}
}
} /**
* 用户生成合并的背景图和css文件的函数
* @param string $dir 生成背景图的图标所在的目录路径
* @param string $bgSavePath 背景图所保存的路径
* @param string $cssSavePath css保存的路径
*/
function generateIconCallback($dir, $bgSavePath, $cssSavePath) {
$shortDir = str_replace('\\', '/', substr($dir, strlen(ROOT-1)));
//返回文件路径信息
$pathInfo = pathinfo($bgSavePath.'.png'); $bgSaveDir = $pathInfo['dirname'];
//确保目录可写
ensure_writable_dir($bgSaveDir);
//背景图名字
$bgName = $pathInfo['filename'];
//调用generateIconCallback_GetFileMap()函数生成每一个图标所需要的数据结构
$fileMap = array('a' => generateIconCallback_GetFileMap($dir)); $iterator = new DirectoryIterator($dir);
foreach ($iterator as $file) {
if ($file->isDot()) continue;
if ($file->isDir()) {
//二级目录也要处理
$fileMap['b-'.$file->getFilename()] = generateIconCallback_GetFileMap($file->getRealPath());
}
}
ksort($fileMap); //分析一边fileMap,计算整个背景图的大小和每一个图标的offset
//初始化偏移量和背景图
$offsetX = $offsetY = $bgWidth = 0;
//设定每个小图标之间的距离
$spaceX =$spaceY = 5;
//图片最大宽度
$maxWidth = 800;
$fileMd5List =array();
//这里需要打印下$fileMap就知道它的数据结构了
foreach ($fileMap as $k1 => $innerMap) {
foreach ($innerMap as $k2 => $itemList) {
//行高姐X轴偏移量初始化
$offsetX = $lineHeight = 0;
foreach ($itemList as $k3 => $item) {
//变量分别是:图标的宽度,高度,类型,文件名,路径,MD5加密字符串
list($imageWidth, $imageHeight, $imageType, $fileName, $filePathname, $fileMd5) = $item;
$fileMd5List []= $fileMd5;
//如果图片的宽度+偏移量 > 最大宽度(800) 那就换行
if ($offsetX !== 0 && $imageWidth + $offsetX > $maxWidth) {
$offsetY += $spaceY + $lineHeight;
$offsetX = $lineHeight = 0;
}
//如果图片高度 > 当前行高 那就讲图片高度付给行高我们这的
if ($imageHeight > $lineHeight) $lineHeight = $imageHeight;
$fileMap[$k1][$k2][$k3] = array($imageWidth, $imageHeight, $offsetX, $offsetY, $imageType, $fileName, $filePathname);
//X轴偏移量的计算
$offsetX += $imageWidth + $spaceX;
if ($offsetX > $bgWidth) $bgWidth = $offsetX;
}
//Y轴偏移量的计算
$offsetY += $lineHeight + $spaceY;
}
}
//把右下两边多加了的空白距离给干掉
$bgWidth -= $spaceX;
$bgHeight = $offsetY - $spaceY;
$fileMd5List = implode("\n", $fileMd5List); //生成背景图和 css文件 //资源路径
$resBaseUrl = RES_BASE_URL;
$suffix = base_convert(abs(crc32($fileMd5List)), 10, 36);
$writeHandle = fopen($cssSavePath, 'w');
fwrite($writeHandle, "/** bg in dir: $shortDir/ */\n[icon-$bgName]{background:url({$resBaseUrl}/$bgName.png?$suffix) no-repeat;display:inline-block;}"); //做图片,这些函数具体可以查看PHP手册
$destResource = imagecreatetruecolor($bgWidth, $bgHeight);
imagealphablending($destResource, false);
imagesavealpha($destResource, false);
$color = imagecolorallocatealpha($destResource, 255, 255, 255, 127); imagefill($destResource, 0, 0, $color); //对每一张小图片进行处理,生成在大背景图里,并生成css文件
foreach ($fileMap as $innerMap) {
foreach ($innerMap as $itemList) {
foreach ($itemList as $item) {
list($imageWidth, $imageHeight, $offsetX, $offsetY, $imageType, $fileName, $filePathname) = $item;
if ($imageType === IMAGETYPE_PNG) {
$srcResource = imagecreatefrompng($filePathname);
} else if ($imageType === IMAGETYPE_JPEG) {
$srcResource = imagecreatefromjpeg($filePathname);
}
imagecopy($destResource, $srcResource, $offsetX, $offsetY, 0, 0, $imageWidth, $imageHeight);
imagedestroy($srcResource); //写入css
$posX = $offsetX === 0 ? 0 : "-{$offsetX}px";
$posY = $offsetY === 0 ? 0 : "-{$offsetY}px";
fwrite($writeHandle, "\n[icon-$bgName=\"$fileName\"]{width:{$imageWidth}px;height:{$imageHeight}px;background-position:$posX $posY;}");
}
}
} //压缩级别 7
imagepng($destResource, "$bgSavePath.png", 7);
imagedestroy($destResource);
fclose($writeHandle); $shortCssSavePath = substr($cssSavePath, strlen(ROOT));
} /**
* 将图片的信息处理成我们想要的数据结构
* @param [type] $dir [description]
* @return [type] [description]
*/
function generateIconCallback_GetFileMap($dir) {
$map = $sort = array();
$iterator = new DirectoryIterator($dir);
foreach($iterator as $file) {
if(!$file->isFile()) continue;
$filePathname = str_replace("\\", '/', $file->getRealPath());
//这些函数可以查看PHP手册
$imageInfo = getimagesize($filePathname);
$imageWidth = $imageInfo[0];
$imageHeight = $imageInfo[1];
$imageType = $imageInfo[2]; if(!in_array($imageType, array(IMAGETYPE_JPEG, IMAGETYPE_PNG))) {
$fileShortName = substr($filePathname, strlen(ROOT) - 1);
echo "<p> $fileShortName 图片被忽略: 因为图片类型不是png|jpg.</p>";
continue;
} //这是我们的图片规格,行高分别有 16 32 64 128 256 99999
foreach(array(16, 32, 64, 128, 256, 99999) as $height) {
if($imageHeight <= $height) {
$mapKey = $height;
break;
}
}
if(!isset($map[$mapKey])) $map[$mapKey] = array();
$filePathInfo = pathinfo($filePathname);
$map[$mapKey] []= array($imageWidth, $imageHeight, $imageType, $filePathInfo['filename'], $filePathname, md5_file($filePathname));
$sort[$mapKey] []= str_pad($imageHeight, 4, '0', STR_PAD_LEFT) . $filePathInfo['filename'];
}
foreach($map as $k => $v) array_multisort($map[$k], SORT_ASC, SORT_NUMERIC, $sort[$k]);
ksort($map, SORT_NUMERIC);
return $map;
} /**
* 判断目录是否可写
* @param string $dir 目录路径
*/
function ensure_writable_dir($dir) {
if(!file_exists($dir)) {
mkdir($dir, 0766, true);
@chmod($dir, 0766);
@chmod($dir, 0777);
}
else if(!is_writable($dir)) {
@chmod($dir, 0766);
@chmod($dir, 0777);
if(!@is_writable($dir)) {
throw new BusinessLogicException("目录不可写", $dir);
}
}
} generateIcon();
?>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/Pink.css">
<title></title> </head>
<body>
<div>我们直接引入所生成的css文件,并测试一下是否成功</div>
<br>
<div>这里在span标签 添加属性 icon-Pink ,值为About-40,正常显示图片</div>
<span icon-Pink="About-40"></span>
</body>
</html>

  调用以上代码,我们的浏览器是这样显示的:

  然后css目录生成了Pink.css文件:

  

  img目录下生成了Pink.png文件:

 

  看看生成的背景图是长啥样子:

  接下来我们再看一下所生成的图片大小与Pink文件夹里所有小图片总和的大小,对它们做个比较:

  

  从上图可以看出,我们生成的图片的大小明显小于文件夹所有图片的大小,所以在将100个小图标下载下来的速度 会明显小于 将背景图下载下来和将CSS下载下来的速度。

  当访问量大时,或者小图片的量大时,会起到很明显的优化效果!!!

  

  代码中的每一个点都基本上有注释,很方便大家去理解,只要大家用心去看,肯定能将这一网站优化技术用到自己的项目中。

  本次博文就写到这!!!

  如果此博文中有哪里讲得让人难以理解,欢迎留言交流,若有讲解错的地方欢迎指出。

  如果您觉得您能在此博文学到了新知识,请为我顶一个,如文章中有解释错的地方,欢迎指出。

  互相学习,共同进步!

减少HTTP请求之合并图片详解(大型网站优化技术)的更多相关文章

  1. 减少HTTP请求之将图片转成二进制并生成Base64编码,可以在网页中通过url查看图片(大型网站优化技术)

    在网站开发过程中,对于页面的加载效率一般都想尽办法求快.那么,怎么让才能更快呢?减少页面请求 是一个优化页面加载速度很好的方法.上一篇博文我们讲解了 “利用将小图标合成一张背景图来减少HTTP请求”, ...

  2. npm安装vue详细教程(图片详解)

    npm安装vue详细教程(图片详解) 一.总结 一句话总结:整个安装流程照着教程来,注意系统环境变量的配置,注意一下npm的本地仓库和缓存位置 教程 系统环境变量 仓库 缓存 1.什么情况下最适合用n ...

  3. Python网络请求urllib和urllib3详解

    Python网络请求urllib和urllib3详解 urllib是Python中请求url连接的官方标准库,在Python2中主要为urllib和urllib2,在Python3中整合成了urlli ...

  4. Android网络请求框架AsyncHttpClient实例详解(配合JSON解析调用接口)

    最近做项目要求使用到网络,想来想去选择了AsyncHttpClient框架开进行APP开发.在这里把我工作期间遇到的问题以及对AsyncHttpClient的使用经验做出相应总结,希望能对您的学习有所 ...

  5. day78_淘淘商城项目_11_单点登录系统实现 + 用户名回显 + ajax请求跨域问题详解_匠心笔记

    课程计划 1.SSO注册功能实现 2.SSO登录功能实现 3.通过token获得用户信息 4.ajax跨域请求解决方案--jsonp 1.服务接口实现   SSO系统就是解决分布式环境下登录问题的,本 ...

  6. CSRF(跨站请求伪造攻击)漏洞详解

    Cross-Site Request Forgery(CSRF),中文一般译作跨站点 请求伪造.经常入选owasp漏洞列表Top10,在当前web漏洞排行中,与XSS和SQL注入并列前三.与前两者相比 ...

  7. P2P技术详解(三):P2P技术之STUN、TURN、ICE详解

    1.内容概述 在现实Internet网络环境中,大多数计算机主机都位于防火墙或NAT之后,只有少部分主机能够直接接入Internet.很多时候,我们希望网络中的两台主机能够直接进行通信,即所谓的P2P ...

  8. HTTP协议探究(六):H2帧详解和HTTP优化

    一 复习与目标 1 复习 HTTP1.1存在的问题 HTTP2.0要兼容HTTP1.1 HTTP2.0的重要概念 分帧层 二进制:流 消息 帧 流的状态.优先级和并发 流量控制 服务器推送 首部压缩 ...

  9. Linux常用命令详解(week1_day1_3)--技术流ken

    本节内容 pidofpstopipuptimewgetcurltrddtargrepfind 命令详解 1.pidof 获取正在运行程序的PID 实例1: [root@ken ~]# pidof ss ...

随机推荐

  1. paip.php-gtk 桌面程序 helloworld总结

    paip.php-gtk 桌面程序 helloworld总结 作者Attilax ,  EMAIL:1466519819@qq.com  来源:attilax的专栏 地址:http://blog.cs ...

  2. applicationContext.xml详解

    http://blog.csdn.net/heng_ji/article/details/7022171

  3. PHP学习笔记1-常量,函数

    常量:使用const(php5)声明,只能被赋值一次,php5以下版本使用define: <?php const THE_VALUE = 100;//PHP5中才有const echo THE_ ...

  4. 【 D3.js 入门系列 — 2.1 】 选择、插入、删除元素

    1. select 和 selectAll 的区别 在 D3 中,选择元素的函数有两个:select 和 selectAll,它们的使用非常重要.先说明一下它们的区别: select 是选择所有指定元 ...

  5. web Listener

    在web应用内部会不断地发生各种事件例如,web应用被启动,web应用停止,用户session开始,用户session 结束,用户请求到达,通常这些事件对开发者而言是透明的.实际上ServletAPI ...

  6. VC命令行编译参数介绍

    CL.exe是控制Microsoft C和C++编译器与链接器的32位工具.编译器产生通用对象文件格式(COFF)对象(.obj)文件.链接器产生可执行文件(.exe)或动态链接库文件(DLL). 注 ...

  7. JAVA ANDROID SOCKET通信检测(SERVER)连接是否断开

    Pre 解决思路 代码后记: 客户端app上的部分代码 调用: 服务器上: 客户端判断服务器是否还活着代码: PRE 在利用socket写通讯程序的时候,想检测服务器是否还活着. 从网上找了很多资料, ...

  8. c-大量经典的c算法---ShinePans

    经典的100个c算法 算法  题目:古典问题:有一对兔子.从出生后第3个月起每一个月都生一对兔子.小兔 子长到第三个月后每一个月又生一对兔子,假如兔子都不死,问每一个月的兔子总数 为多少? _____ ...

  9. eclipse No Default Proposals 无提示

    链接地址:http://blog.csdn.net/rogerjava/article/details/5689785 今天特抑郁,早上开机后发现eclipse的代码提示功能不好使了,Alt+/ 这么 ...

  10. C-最长回文子串(1)

    最长回文子串,就是在字符串中找到最长的对称的子串. s是一个字符串. int max = 0; for(i = 0;i<m;i++) for(j = i;j<m;j++) if(s[i.. ...