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
所能獲淂的增加效能其實很有限。
範例下載