zoukankan      html  css  js  c++  java
  • 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);
                }
            }
        }
    }

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


     

    欢迎大家阅读我的极客时间专栏《Java业务开发常见错误100例》【全面避坑+最佳实践=健壮代码】
  • 相关阅读:
    常见业务指标
    1006 换个格式输出整数 (Python)
    1004 成绩排名 (Python)
    1003 我要通过! (Python)
    1008 数组元素循环右移问题 (Python)
    如何使用SSH秘钥链接Github
    在windows下如何正确安装curses模块
    面向数据结构C基础知识点(个人向)
    用Python实现链式调用
    python重点串讲
  • 原文地址:https://www.cnblogs.com/lovecherry/p/1701997.html
Copyright © 2011-2022 走看看