原文:减少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. HDU 4825 Xor Sum 字典树+位运算

    点击打开链接 Xor Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others) ...

  2. c++,内联成员函数

    内联成员函数有两程方式实现内联成员函数1)在声名成员函数的同时定义成员函数体2)声明成员函数时,在最前面加上inline关键字在定义成员函数时也在最前面加上inline关键字 建议inline函数在头 ...

  3. grub2的/etc/default/grub文件详解

    # If you change this file, run 'update-grub' afterwards to update# /boot/grub/grub.cfg.GRUB_DEFAULT= ...

  4. 显示器中关于HS,VS,HBP,VBP参数浅析

    先来解释几个缩写的含义 HSYNC : 水平同步信号(horizontal synchronization signal) VSYNC :  垂直同步(Vertical Sync):场同步 FR : ...

  5. dotnet tools 运行 dotnet run

    dotnet tools 运行 dotnet run dotnet run 命令介绍 前言 本篇主要介绍 asp.net core 中,使用 dotnet tools 运行 dotnet run 之后 ...

  6. 设计模式(九)外观模式Facade(结构型)

    设计模式(九)外观模式Facade(结构型) 1. 概述 外观模式,我们通过外观的包装,使应用程序只能看到外观对象,而不会看到具体的细节对象,这样无疑会降低应用程序的复杂度,并且提高了程序的可维护性. ...

  7. 基于SOAP的xml网络交互心得

    感谢小二同学将遇到的问题分享给我们,再此给以掌声.如果看不懂下面文章的建议查找一下HTTP协议的文艺,对HTTP协议要有个概念. XML网络交互心得 目录 一.     xml解析 1.根路径下 2. ...

  8. hdu1853解题报告

    题意和解决回路匹配的思路如同hdu3488 (这里我第一次想到最短路,但是对于有回路这个不知道怎么处理,后来看了别人的解题报告才知道KM匹配,但是看到KM之后就自己想...想了很久....还是不知道回 ...

  9. 查看htmlView

    1.视图 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:too ...

  10. Fragment使用

    当我们需要动态的多界面切换的时候,就需要将UI元素和Activity融合成一个模块.在2.3中我们一般通过各种Activity中进行跳转来实现多界面的跳转和单个界面动态改变.在4.0或以上系统中就可以 ...