`
天梯梦
  • 浏览: 13732614 次
  • 性别: Icon_minigender_2
  • 来自: 洛杉矶
社区版块
存档分类
最新评论

减少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>

  

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

0064cTs2jw1eyeiz5vmwzj30lg04zaal

 

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

0064cTs2jw1eyeiz609mhj30ak05raac

 

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

0064cTs2jw1eyeiz72b19j30dg08474s

 

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

0064cTs2jw1eyeiz8v9oij30p806bq53

 

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

0064cTs2jw1eyeiz7m85hj30d609jwfb

0064cTs2jw1eyeiz838hbj30ih09vtaf

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

 

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

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

 

本次博文就写到这!!!

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

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

  互相学习,共同进步!

 

 

原文:http://www.cnblogs.com/it-cen/p/4618954.html

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

 

 

 

 

 

分享到:
评论

相关推荐

    大型网站性能优化实战:从前端、网络、CDN到后端、大促的全链路性能优化详解.docx

    大型网站性能优化实战:从前端、网络、CDN 到后端、大促的全链路性能优化详解 本文档详细介绍了大型网站性能优化的重要性和价值,以及如何从前端、网络、CDN 到后端、大促等多个方面进行性能优化。性能优化是提高...

    Java 大型网站性能优化实战从前端网络 CDN 到后端大促的全链路性能优化

    另外,前端优化还包括代码压缩和合并,通过压缩JavaScript和CSS文件减小传输大小,合并多个文件以减少HTTP请求。此外,利用懒加载技术,可以按需加载非首屏内容,进一步提高首屏加载速度。利用HTTP/2的多路复用功能...

    详解网站中图片日常使用以及优化手法

    1. **减少HTTP请求** - 通过合并图片(如雪碧图)、使用Base64编码或采用WebP格式,可以减少网络请求次数,提高页面加载效率。 2. **减小图片大小** - 通过压缩工具(如TinyPNG、JPEGmini等)压缩图片,减小文件...

    asp.net 网站优化

    - **减少HTTP请求**:通过合并CSS和JavaScript文件、使用图片精灵等技术来减少页面加载时的HTTP请求次数,从而加快页面加载速度。 - **异步处理**:利用ASP.NET AJAX或SignalR等技术实现页面部分刷新,避免整个页面...

    css精灵图片例子.pdf

    CSS精灵图片(Sprites)是一种优化网页性能的技术,通过将多个小图片合并成一张大图,然后利用CSS背景定位(background-position)来展示需要显示的部分,以此减少HTTP请求,提高网页加载速度。CSS Sprites在处理...

    autoptimize.2.3.4自动优化

    1. **CSS整合与压缩**:Autoptimize能够合并多个CSS文件为一个,减少HTTP请求次数,同时对CSS代码进行压缩,去除不必要的空格和注释,降低文件大小。 2. **JavaScript优化**:类似地,插件会将分散的JavaScript文件...

    高性能网站建设指南.pdf

    - **案例1:减少HTTP请求**:例如,将多个小图标合并成一张大图并通过CSS Sprites技术实现,减少图片请求次数。 - **案例2:使用内容发布网络**:对于大型网站而言,使用CDN可以显著减少跨区域访问延迟,提高全球...

    csssprites_3.2.1

    在网页开发中,CSS精灵(CSS Sprites)是一种高效优化页面加载速度的技术,通过将多个小图合并成一张大图,然后利用CSS背景定位的方式来显示需要的部分,以此减少HTTP请求,加快页面加载。"csssprites_3.2.1.zip"这...

    CSSSprite示例代码

    这种技术尤其适用于那些需要频繁加载的小图标或者背景图片,减少了HTTP请求的数量,从而提升了页面的加载速度。 **一、Sprite技术原理** 1. **合并图像**:将网页中多个小图整合到一张大图上,通常称为"精灵图"。 ...

    面向程序员的数据库访问性能优化法则

    合理使用分区技术减少查询数据量。 - **案例分析**:假设有一个订单表,包含数百万条记录,如果查询条件不使用索引,则会导致全表扫描,消耗大量时间。通过添加适当的索引,可以显著减少查询所需的时间。 **2. 返回...

    HTTP2.0协议

    - **合并技术(Spriting)**:通过将多个图像合并到一个文件中来减少HTTP请求的数量。 - **内联技术(Inlining)**:将某些资源(如图片或样式表)直接嵌入HTML文档中,以减少额外的请求。 - **拼接技术...

    windows系统下的nginx1.4.1,集成了淘宝的concat模块

    2. **HTTP Concatenation (http-concat)**:这个模块允许Nginx在服务静态资源时,将多个CSS或JS文件合并成一个文件,从而减少HTTP请求的数量,提高页面加载速度,遵循了减少HTTP往返次数的性能优化原则。 3. **源码...

    Oracle PGA详解

    ### Oracle PGA详解:深入理解与优化策略 #### PGA概述与重要性 在Oracle数据库系统中,PGA(Program Global Area)是每个服务器进程所拥有的专用内存区,它为每个会话提供私有数据结构和控制信息。PGA的重要性...

    SQL 查询语句优化

    ### SQL查询语句优化策略详解 #### 一、选择最有效率的表名顺序 在基于规则的优化器中,如Oracle,SQL语句中表的处理顺序至关重要。Oracle的解析器遵循从右至左的处理逻辑,因此,位于FROM子句最右侧的表将被最先...

    Css Sprite

    **CSS Sprite技术详解** CSS Sprite,也被称为精灵图或雪碧图,是一种在网页设计中广泛应用的优化图像加载的技术。它的主要目的是减少网页HTTP请求的数量,从而提高页面加载速度,优化用户体验。通过将多个小图标...

    详解优化iOS程序性能的25个方法

    19. 优化网络请求:合并多个小请求为一个大请求,或者使用HTTP/2以减少网络延迟。 20. 使用轻量级的对象池:对于短生命周期且频繁创建销毁的对象,可以使用对象池来复用,减少内存分配和释放。 21. 限制KVO(Key-...

    Unity优化建议

    ### Unity优化建议详解 #### 一、Unity脚本系统优化 Unity引擎因其强大的跨平台能力,在游戏开发领域占据着重要地位。为了确保游戏在不同设备上都能流畅运行,对Unity项目进行性能优化变得尤为重要。其中,脚本...

Global site tag (gtag.js) - Google Analytics