[IE8]運用Data URI技術加快網頁載入速度

Data URI技術,簡而言之就是把原本需要額外連線下載的資源,例如圖形檔、Object Data等資源,一併結合在HTML中下載。

 

運用Data URI技術加快網頁載入速度
 
 
/黃忠成
 
 
如何加快網頁載入速度
 
    加快網頁載入速度的方法有很多,從壓縮JavaScript到壓縮HTML都是方法之一,在Internet Explorer 8中,除了以上這兩種方法外,我們還可以透過Data URI技術來達到加快網頁載入速度的目的。
Data URI技術,簡而言之就是把原本需要額外連線下載的資源,例如圖形檔、Object Data等資源,一併結合在HTML中下載。舉個例來說,當我們有一個網頁,裡面以數十個<img>方式連結了圖形,
對於瀏覽器而言,它會先載入HTML,然後一一解析<img> Tag,然後一一照其src指定的URL下載圖形,這原本是很常見且很直覺的處理方式,但是這種方式對於網頁效能而言,會造成不小的影響,
因為每一次解析<img>後,瀏覽器就會發出一次連線需求來下載圖形,當有數十個<img>時,自然會在繪製網頁期間發出相等的連線要求,加上瀏覽器多半有著最大同時連線數(大概是2個)的限制,
所以這種傳統模式自然對於網頁載入速度上產生影響。
 
    Data URI技術就是為了解決此問題而來,我們可以在<img>的src屬性中直接以base64編碼方式,將圖形的2進位碼硬嵌入HTML中,這樣瀏覽器在繪製該<img>時,省卻了一個連線數及圖形下載量,
網頁載入速度自然會有所提升。下面是一個Data URI應用例子:
<html>
<head>
<title>Show Data URI in the IMAGE tag</title>
<meta http-equiv="X-UA-Compatible" content="IE=7">
</head>
<body>
Draws a small red square.
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAFSDNYfAAAAaklEQVR42u3XQQrAIAwAQeP%2F%2F6wf8CJBJTK9lnQ7FpHGaOurt1I34nfH9pMMZAZ8BwMGEvvh%2BBsJCAgICLwIOA8EBAQEBAQEBAQEBK79H5RfIQAAAAAAAAAAAAAAAAAAAAAAAAAAAID%2FABMSqAfj%2FsLmvAAAAABJRU5ErkJggg%3D%3D">fallback</img>
</body>
</html>
當然,這種技術也不是全然無缺點,由於使用了內嵌的模式,瀏覽器原本對於網頁圖形的快取功能會失效,以一般的網頁及使用Data URI 的網頁來對比,
在使用者第一次造訪時會發現Data URI的網頁比較快,但在第二次造訪時,由於沒有快取機制存在,所以傳統未使用Data URI的網頁載入速度會比使用
Data URI的網頁快上許多(因為不用再下載圖形)。
那除了<img>外,Data URI還可以用在那呢?就目前最常用的情況來說,Data URI技術可以應用在<img>及<object>兩個Tag中,但是由於Internet Explorer 8
對於Data URI的內嵌內容大小限制在32K,而32K對於<object> tag來說實在太小了,因此真正實用的就只是<img>而已。
 
 
ASP.NET DataURIImage Server Control
 
 由於一般很少用到Data URI,加上Internet Explorer直到8.0後對於Data URI的支援才完整一些,因次假設你是使用ASP.NET撰寫程式,那麼要使用Data URI來顯示圖形,你就得在程式中讀取圖形檔,
然後轉換為Base64後設給Image控制項的ImageUrl屬性。
 為了讓讀者們能更方便的使用Data URI來顯示圖形,我特別開發了一個DataURIImage控制項,她的用法與Image控制項完全相同,不同的是其在輸出時會以Base64將圖形檔內嵌至HTML中,
以下是該控制項的原始碼。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
 
namespace DataUriImage
{
    [DefaultProperty("Text")]
    [ToolboxData("<{0}:DataUriImage runat=server></{0}:DataUriImage>")]
    public class DataUriImage : WebControl
    {
 
        public string ImageUrl
        {
            get
            {
                object o = ViewState["ImageUrl"];
                if (o == null)
                    return string.Empty;
                return (string)o;
            }
            set
            {
                ViewState["ImageUrl"] = value;
            }
        }
 
        public ImageAlign ImageAlign
        {
            get
            {
                object o = ViewState["ImageAlign"];
                if (o == null)
                    return ImageAlign.NotSet;
                return (ImageAlign)o;
            }
            set
            {
                ViewState["ImageAlign"] = value;
            }
        }
 
 
        public string AlternateText
        {
            get
            {
                object o = ViewState["AlternateText"];
                if (o == null)
                    return string.Empty;
                return (string)o;
            }
            set
            {
                ViewState["AlternateText"] = value;
            }
        }
 
        public string DescriptionUrl
        {
            get
            {
                object o = ViewState["DescriptionUrl"];
                if (o == null)
                    return string.Empty;
                return (string)o;
            }
            set
            {
                ViewState["DescriptionUrl"] = value;
            }
        }
 
        [Browsable(false)]
        public override bool Enabled
        {
            get
            {
                return base.Enabled;
            }
            set
            {
                base.Enabled = value;
            }
        }
 
       [Browsable(false)]
        public override FontInfo Font
        {
            get
            {
                return base.Font;
            }
        }
 
        protected override HtmlTextWriterTag TagKey
        {
            get
            {
                return HtmlTextWriterTag.Img;
            }
        }
 
        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            string data;
            base.AddAttributesToRender(writer);
            if (Page.Request.Browser.Browser.Contains("IE") &&
                Convert.ToDouble(Page.Request.Browser.Version) >= 8)
            {
                using (FileStream fs =
                  new FileStream(HttpContext.Current.Request.MapPath(ImageUrl),
                  FileMode.Open, FileAccess.Read))
                {
                    byte[] buff = new byte[32768];
 
                    using (BinaryReader br = new BinaryReader(fs))
                    {
                        int size = br.Read(buff, 0, 32768);
                        MemoryStream ms = new MemoryStream();
                        try
                        {
                            ms.Write(buff, 0, size);
                            data = Convert.ToBase64String(ms.GetBuffer());
                            if (data.Length > 32768)
                                throw new Exception(
                                  "image is too large to embed as Data Uri.");
                        }
                        finally
                        {
                            ms.Dispose();
                        }
                    }
 
                }
 
                if (ImageUrl.EndsWith(".bmp"))
                    writer.AddAttribute(HtmlTextWriterAttribute.Src,
                                 "data:image/bmp;base64, " + data);
                else if (ImageUrl.EndsWith(".jpg"))
                    writer.AddAttribute(HtmlTextWriterAttribute.Src,
                                 "data:image/jpg;base64, " + data);
                else if (ImageUrl.EndsWith(".png"))
                    writer.AddAttribute(HtmlTextWriterAttribute.Src,
                        "data:image/png;base64, " + data);
                else if (ImageUrl.EndsWith(".gif"))
                    writer.AddAttribute(HtmlTextWriterAttribute.Src,
                       "data:image/gif;base64, " + data);
                else
                    throw new Exception("image not support.");
            }
            else
                writer.AddAttribute(HtmlTextWriterAttribute.Src,
                     ResolveClientUrl(ImageUrl));
            if (ImageAlign != ImageAlign.NotSet)
                writer.AddAttribute(HtmlTextWriterAttribute.Align,
                    ImageAlign.ToString().ToLower());
            if (AlternateText.Length > 0)
                writer.AddAttribute(HtmlTextWriterAttribute.Alt, AlternateText);
            if (DescriptionUrl.Length > 0)
                writer.AddAttribute(HtmlTextWriterAttribute.Longdesc,
                  ResolveClientUrl(DescriptionUrl));
        }
    }
}
安裝此控制項的方式很簡單,請下載文後範例檔案,裡面有一個DataURIImage目錄,開啟Visual Studio 2008後,開啟一個新的ASP.NET專案,接著在工具列上新增一個Ext的頁籤,
然後按滑鼠右鍵來新增控制項。
接著選擇DataURIImage這個Assembly
按下開啟然後再按下確定按紐後,DataURIImage控制項就安裝在你的Visual Studio 2008中了,下面是使用DataURIImage控制項的例子。

 

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>
 
<%@ Register Assembly="DataUriImage" Namespace="DataUriImage" TagPrefix="cc1" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <cc1:DataUriImage ID="DataUriImage1" runat="server"
            ImageUrl="~/up_green24_d.bmp" AlternateText="TEST"   Width="24px" />
    </div>
    </form>
</body>
</html>
執行時,你應可於IE8上看到圖形,當使用View Source(查看原始碼)時,你會發現Data URI的蹤跡。
 
 
後記
 
    就個人觀點而言,Data URI應是網頁調校中的最後手段,除非網頁上真的使用了很多小圖形,否則使用Data URI
所能獲淂的增加效能其實很有限。
 
範例下載