首先,感谢武眉博<活靶子.Net>的博客[2007最后一博]Url地址重写,利用HttpHander手工编译页面并按需生成静态HTML文件给我提供了很大的帮助。
最近,项目中DesignStudio页面的初始加载速度比较慢,想要提升一下页面加载的性能。
Team对代码做了很多改进,包括减少数据库访问,加Cache等,最终还是没能达到客户的满意。
然后浏览了很多国内外的e-commerce网站,发现大多数都使用HTML页面来提升页面加载性能。
最终决定尝试变换思路,用HTML页面来提升性能,以满足客户需求。
由于每个Item的Personalize功能都会使用DesignStudio页面,只是改变一下页面的QueryString,所以只有核心部分的生成HTML功能采纳了武眉博的代码思路,其他的业务逻辑还是依据项目的实际情况进行设计。
下面把我觉得在开发过程中需要注意的地方记录下来,希望能帮助有可能涉及到这方面开发工作的朋友。如有任何错误,还请不吝赐教。
1.上面提到由于所有的Item都会使用DesignStudio页面进行Personalize,所以我直接将这个页面映射到一个HttpHandler,免去了httpModules和UrlMappings的配置。
<httpHandlers> <add path="DesignStudio.aspx" verb="*" type="AspxBoy.BuildHtmlDemo.ToHtmlHandler"/> </httpHandlers>
2.由于是通过QueryString判断是否生成HTML文件,所以可以直接使用context对象获取QueryString就OK了。
3.BuildManager.CreateInstanceFromVirtualPath方法的第一个参数需要传入不带QueryString的aspx页面路径。
public void ProcessRequest(HttpContext context) { string rawUrl = context.Request.RawUrl; string htmlPath = context.Request.PhysicalApplicationPath; string path = context.Request.Path; string folder = "StaticHTML"; string paramAction = context.Request.QueryString["ACTION"]; string sku = context.Request.QueryString["SKU"]; string filename = folder + "\\" + sku + ".html"; if (!Directory.Exists(htmlPath + folder)) { Directory.CreateDirectory(htmlPath + folder); } if (!string.IsNullOrWhiteSpace(paramAction)) { if (paramAction.ToLower() == "create") { string htmlFile = htmlPath + filename; if (!File.Exists(htmlFile)) { IHttpHandler handler = BuildManager.CreateInstanceFromVirtualPath(path, typeof (Page)) as IHttpHandler; handler.ProcessRequest(context); context.Response.Filter = new Filter(context.Response.Filter, htmlFile); } else { context.Server.Transfer(filename); } } else if (paramAction.ToLower() == "edit") { IHttpHandler handler = BuildManager.CreateInstanceFromVirtualPath(path, typeof (Page)) as IHttpHandler; handler.ProcessRequest(context); } } }
public class Filter : Stream { private Stream _sink; private long _position; private string filePath; private FileStream _tempFileStream; private BinaryWriter _writer; byte[] _buffer; int _count; public Filter(Stream sink, string file) { _sink = sink; filePath = file; } public override bool CanRead { get { return true; } } public override bool CanSeek { get { return true; } } public override bool CanWrite { get { return true; } } public override long Length { get { return 0; } } public override long Position { get { return _position; } set { _position = value; } } public override long Seek(long offset, System.IO.SeekOrigin direction) { return _sink.Seek(offset, direction); } public override void SetLength(long length) { _sink.SetLength(length); } public override void Close() { if (_sink != null) _sink.Close(); if (_tempFileStream != null) _tempFileStream.Close(); if (_writer != null) _writer.Close(); } public override void Flush() { _sink.Flush(); } public override int Read(byte[] buffer, int offset, int count) { return _sink.Read(buffer, offset, count); } private FileStream TempFileStream { get { if (_tempFileStream == null) { _tempFileStream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write); } return _tempFileStream; } } private BinaryWriter BWriter { get { if (_writer == null) { string fileDirectory = filePath.Substring(0, filePath.LastIndexOf("\\")); if (!Directory.Exists(fileDirectory)) { Directory.CreateDirectory(fileDirectory); } _writer = new BinaryWriter(TempFileStream); } return _writer; } } public override void Write(byte[] buffer, int offset, int count) { if (HttpContext.Current.Response.ContentType == "text/html") { Encoding e = Encoding.GetEncoding(HttpContext.Current.Response.Charset); string pageStr = e.GetString(buffer, offset, count); _buffer = e.GetBytes(pageStr); _count = e.GetByteCount(pageStr); _sink.Write(_buffer, 0, _count); BWriter.Write(_buffer, 0, _count); } else { _sink.Write(buffer, offset, count); } } }