httpmodule和httphandler配合的又一应用——合并脚本样式

 

 

页面上数十个脚本和样式文件怎么去合并又少写人工干预?

 

首先,使用查找替换把所有的<script>和<link>替换为<resource>然后在<resrouce>中加上runat="server":

 

母板页:

 

 

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site1.master.cs" Inherits="StaticResourceMerge.DemoWebApp.Site1" %>

<!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">
    <resource src='http://images.xxx.com/js/script/apiCommon.js' type="text/javascript"
        runat="server"></resource>
    <resource language="javascript" type="text/javascript" src="http://images001.xxx.com/js/my/v3/1m/site_sms_messages.js"
        charset="utf-8" runat="server"></resource>
    <resource href='http://images.xxx.com/css/0709/ibuyandisell.css' type="text/css"
        rel="stylesheet" runat="server" />
    <title></title>
    <asp:ContentPlaceHolder ID="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        母板页
        <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
        </asp:ContentPlaceHolder>
    </div>
    </form>
</body>
</html>


 

 

用户控件:

 

 

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs"
    Inherits="StaticResourceMerge.DemoWebApp.WebUserControl1" %>

<resource language="JavaScript" type="text/javascript" src="http://images.xxx.com//js/jquery.1.3.2.js" runat="server"></resource>

<resource language="JavaScript" type="text/javascript" src="http://images.xxx.com//js/scroll.js" runat="server"></resource>

用户控件

 

页面:

 

<%@ Page Title="" Language="C#" MasterPageFile="~/Site1.Master" AutoEventWireup="true"
    CodeBehind="WebForm1.aspx.cs" Inherits="StaticResourceMerge.DemoWebApp.WebForm1" %>

<%@ Register Src="WebUserControl1.ascx" TagName="WebUserControl1" TagPrefix="uc1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
    <resource href='http://images.xxx.com/css/gamestytle.css' rel="stylesheet" type="text/css"
        runat="server" />
    <resource rel="stylesheet" type="text/css" href="http://images001.xxx.com/css/header/header_v32.css"
        runat="server" />

    <resource language="JavaScript" type="text/javascript" src="http://images.xxx.com//js/jquery.1.3.2.js" runat="server"></resource>

</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <resource language="javascript" type="text/javascript" src="http://images.xxx.com/JS/JScript/FootV2.js"
        runat="server"></resource>
    <resource rel="stylesheet" type="text/css" href="http://images001.xxx.com/css/my/v3/1m/site_sms_messages.css"
        runat="server" />
    <div>
        页面
    </div>
    <uc1:WebUserControl1 ID="WebUserControl11" runat="server" />
</asp:Content>


 

 

配置文件加上httomodule和handler:

 

 

<?xml version="1.0"?>
<configuration>
  <appSettings/>
  <connectionStrings/>
  <system.web>
    <!--
            设置 compilation debug="true" 可将调试符号插入
            已编译的页面中。但由于这会
            影响性能,因此只在开发过程中将此值
            设置为 true。
        -->
    <compilation debug="true">
    </compilation>
    <!--
            通过 <authentication> 节可以配置 ASP.NET 用来
            识别进入用户的
            安全身份验证模式。
        -->
    <authentication mode="Windows"/>
    <!--
            如果在执行请求的过程中出现未处理的错误,
            则通过 <customErrors> 节可以配置相应的处理步骤。具体说来,
            开发人员通过该节可以配置
            要显示的 html 错误页
            以代替错误堆栈跟踪。

        <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
            <error statusCode="403" redirect="NoAccess.htm" />
            <error statusCode="404" redirect="FileNotFound.htm" />
        </customErrors>
        -->

    <httpModules>
      <add name="StaticResourceMergeResourceCollectorModule" type="StaticResourceMerge.Core.ResourceCollectorModule, StaticResourceMerge.Core"/>
    </httpModules>
    <httpHandlers>
      <add path="ResourceHandler.ashx" type="StaticResourceMerge.Core.ResourceHandler, StaticResourceMerge.Core" verb="GET,HEAD"/>
    </httpHandlers>
  </system.web>
</configuration>


 

 

 

在httpmodule中可以遍历找出页面上所有静态资源然后生成合并后的script和style

 

代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Diagnostics;
using System.Web.Security;

namespace StaticResourceMerge.Core
{
    public class ResourceCollectorModule : IHttpModule
    {
        private List<HtmlGenericControl> controls = new List<HtmlGenericControl>();

        public void Dispose()
        {

        }

        public void Init(HttpApplication application)
        {
            application.PreRequestHandlerExecute += new EventHandler(application_PreRequestHandlerExecute);
        }

        private void application_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            HttpContext httpContext = HttpContext.Current;
            Page page = httpContext.Handler as Page;
            if (page != null)
            {
                page.PreRender += new EventHandler(page_PreRender);
            }
        }

        private void page_PreRender(object sender, EventArgs e)
        {
            Stopwatch sw = Stopwatch.StartNew();
            Page page = sender as Page;
            if (page != null)
            {
                FindResource(page);
                if (controls.Count <= 0)
                    return;

                foreach (HtmlGenericControl c in controls)
                {
                    if (c.Parent != null)
                        c.Parent.Controls.Remove(c);
                }

                string pageTypeName = page.GetType().Name;
                if (MergedResourceCache.Data[pageTypeName] == null)
                {
                    StringBuilder stylePath = new StringBuilder();
                    StringBuilder scriptPath = new StringBuilder();
                    List<ResourceItem> rawStyleItems = new List<ResourceItem>();
                    List<ResourceItem> rawScriptItems = new List<ResourceItem>();

                    foreach (HtmlGenericControl c in controls)
                    { 
                        if (!string.IsNullOrEmpty(c.Attributes["href"])
                            && rawStyleItems.Find(d => d.Url == c.Attributes["href"].ToString()) == null)
                        {
                            var item = new ResourceItem
                            {
                                ResourceItemType = ResourceItemType.Style,
                                Url = c.Attributes["href"].ToString()
                            };
                            stylePath.Append(item.Url);
                            rawStyleItems.Add(item);
                        }

                        if (!string.IsNullOrEmpty(c.Attributes["src"])
                           && rawScriptItems.Find(d => d.Url == c.Attributes["src"].ToString()) == null)
                        {
                            var item = new ResourceItem
                            {
                                ResourceItemType = ResourceItemType.Script,
                                Url = c.Attributes["src"].ToString()
                            };
                            scriptPath.Append(item);
                            rawScriptItems.Add(item);
                        }
                    }

                    string styleKey = FormsAuthentication.HashPasswordForStoringInConfigFile(stylePath.ToString(), "md5");
                    string scriptKey = FormsAuthentication.HashPasswordForStoringInConfigFile(scriptPath.ToString(), "md5");

                    RawResourceCache.Data[styleKey] = rawStyleItems;
                    RawResourceCache.Data[scriptKey] = rawScriptItems;

                    ResourceItem mergedStyle = new ResourceItem
                    {
                        ResourceItemType = ResourceItemType.Style,
                        Url = styleKey
                    };

                    ResourceItem mergedScript = new ResourceItem
                    {
                        ResourceItemType = ResourceItemType.Script,
                        Url = scriptKey
                    };

                    MergedResourceCache.Data[pageTypeName] = new List<ResourceItem>();
                    MergedResourceCache.Data[pageTypeName].Add(mergedStyle);
                    MergedResourceCache.Data[pageTypeName].Add(mergedScript);
                }

                if (page.Form != null && MergedResourceCache.Data[pageTypeName] != null)
                {
                    foreach (var item in MergedResourceCache.Data[pageTypeName])
                    {
                        if (item.ResourceItemType == ResourceItemType.Style)
                        {
                            HtmlGenericControl style = new HtmlGenericControl();
                            style.TagName = "link";
                            style.Attributes.Add("href", string.Format("{0}?url={1}", page.ResolveUrl(ConfigProvider.ResourceHandlerPath), item.Url));
                            style.Attributes.Add("rel", "stylesheet");
                            style.Attributes.Add("type", "text/css");
                            page.Form.Controls.AddAt(0, style);
                        }
                        if (item.ResourceItemType == ResourceItemType.Script)
                        {
                            HtmlGenericControl script = new HtmlGenericControl();
                            script.TagName = "script";
                            script.Attributes.Add("src", string.Format("{0}?url={1}", page.ResolveUrl(ConfigProvider.ResourceHandlerPath), item.Url));
                            script.Attributes.Add("type", "text/javascript");
                            page.Form.Controls.AddAt(page.Form.Controls.Count, script);
                        }
                    }
                }
            }
            page.Response.Write(sw.ElapsedMilliseconds);
        }

        private void FindResource(Control c)
        {
            if (c.Controls.Count > 0)
            {
                foreach (Control child in c.Controls)
                    FindResource(child);
            }

            HtmlGenericControl genericControl = c as HtmlGenericControl;
            if (genericControl != null && genericControl.TagName.Equals("resource", StringComparison.InvariantCultureIgnoreCase))
            {
                controls.Add(genericControl);
            }
        }
    }
}

 

 

然后在handler中生成合并后的静态资源:

在这里我们从网络上获取资源可以改为本地,这样速度快点

为了简单这里没有做合并后的文件缓存和输出缓存

 

 

代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
using System.Net;
using System.Threading;

namespace StaticResourceMerge.Core
{
    class ResourceHandler : IHttpHandler
    {
        private StringBuilder data = new StringBuilder();
        private int finished = 0;
        private object locker = new object();

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

        public void ProcessRequest(HttpContext context)
        {
            string key = context.Request.QueryString["url"];
            if (string.IsNullOrEmpty(key)) return;
            List<ResourceItem> items = RawResourceCache.Data[key];
            if (items == null) return;

            foreach (var item in items)
            {
                WebClient wc = new WebClient();
                wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
                wc.DownloadStringAsync(new Uri(item.Url), item.Url);
            }

            while (finished != items.Count)
                Thread.Sleep(100);
            context.Response.Write(data.ToString());
        }

        private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            lock (locker)
            {
                finished++;
                data.AppendFormat("/******** {0} ********/{1}{2}{3}", e.UserState.ToString(), Environment.NewLine, e.Result, Environment.NewLine);
            }
        }
    }
}

 

 

整个测试项目下载:
http://files.cnblogs.com/lovecherry/StaticResourceMerge.rar

 


 

 

posted @ 2010-04-01 10:03 lovecherry 阅读(...) 评论(...) 编辑 收藏