静态页面有访问速度快、容易被搜索引擎注意到等优点,将网站的部分页面转换成静态页面来进行速度和搜索引擎的优化是一种很好的策略。本文将介绍我司生成静态页面的一种解决方案:
ASP.NET的页面生成机制大概如下:
当服务器接收到一个.net页面的Request的时候,将会调用.net的服务模块,然后生成对应页面的一个对象,在其中就有我们所做的数据的操作(比如从数据库检索数据、从Request中提取参数、从Session或Cookie读出或写入内容以及其它的运算),等所有操作做完了以后,服务模块会自动调用一个叫Render的函数,这个函数负责的工作叫呈现,其实就是将服务模块解析出来的HTML输出到客户端。那我们要生成静态页面就得在它身上动动手脚。
现在切入点已经找到,不再茫然无半点头绪,不过,问题也就立刻出现了,既然是个函数是负责将输出写到客户端,那我们怎么载取要输入到客户端的文本内容呢?其实截取很容易,只要在子类重写一下Render就可以了,问题出在,Render的函数原形如下:
protected void Render(System.Web.UI.HtmlTextWriter writer);
HtmlTextWriter是什么东西?这个嘛,可以看一下MSDN,下面是它的类继承图:
System.Object
System.MarshalByRefObject
System.IO.TextWriter
System.Web.UI.HtmlTextWriter
说到这里,我们最好先介绍一下关于.NET的IO模型。.NET的文件IO是以流的形式存在的,即是说,将文件或内存或网络数据传输等当成一个流对象,可以对其进行读取和写入,最基本的几个流操作如下:
1、 可以从流读取。读取是从流到数据结构(如字节数组)的数据传输。
2、 可以向流写入。写入是从数据源到流的数据传输。
3、 流可以支持查找。查找是对流内的当前位置进行查询和修改。
流支持以上全部或部分操作,这要决定于“流”本身的性质,例流,NetworkStreams这个流就不支持查找操作,这是由网络的性质决定的。流的基础类放在System.IO命名空间中。
OK,了解了流的基本概念后,就可以来看看我们究竟要怎么截取生成的HTML了。
从上面的类继承图可以看到,HtmlTextWriter 是System.IO.TextWriter的一个子类。再看看它的构造函数:
public HtmlTextWriter(TextWriter);
哦,需要一个TextWriter做参数,TextWriter是它的基类,其实它是一个抽象类,看来,HtmlTextWriter不想自己定义自己的输出流,而是希望将输出结果送到指定的一个目的地,一般到说,是客户端浏览器,不过,我们现在得将它转转向,将它输出到我们指定的文件就行了。
那我们再看看,一般我们写文件用的是什么类?哦,对了,是StreamWriter类,那查查它的类继承图吧,或许有点帮助:
System.Object
System.MarshalByRefObject
System.IO.TextWriter
System.IO.StreamWriter
哈哈,果然不出所料,它的基础类也是TextWriter,这样就非常好了。那说明我们只要生成一个代表一个文件的一个StreamWriter对象,然后将其做为HtmlTextWriter构造函数的参数,然后生成一个HtmlTextWriter对象,那么这个HtmlTextWriter写入的就是我们的目标文件了。同理,生成一个MemoryStream,然后将其做为HtmlTextWriter构造函数的参数就可以将生成的HTML代码写到我们指定的内存块。
好,那让我们试试看,可以这样做
override protected void Render(System.Web.UI.HtmlTextWriter writer)
{
//MemoryStream ms = new MemoryStream();
//StreamWriter sww = new StreamWriter(ms);
StreamWriter swr = new StreamWriter(Server.MapPath("./") + "text.htm");
System.Web.UI.HtmlTextWriter htmlw = new HtmlTextWriter(swr);
base.Render(htmlw);
htmlw.Flush();
htmlw.Close();
//string strLL = System.Text.Encoding.UTF8.GetString(ms.ToArray());
//Response.Write(strLL);
}
这段代码很好理解,无须多说,如果将输出流换成注释中的那些代码,那HTML输出将放到指定内存块,然后又转存到一个string对象中,最后用Response.Write写到客户端。一个很奇怪的过程,好象是无用功,其实,在这过程中,体现了我们对其能随意操作了。为什么要将之存在内存块里,还要转换成string对象?因为,生成的HTML中,有ViewState,和一些无用的东西,我们最好将之去掉再输出。
这样子,每次请求这张页面就再也不是在浏览器显示,而是写入了文件或我们指定的内存。
---------------------------------------------------------
输出生成好的aspx页面
---------------------------------------------------------
当然你也可以在任意事件后生成静态页:
Response.Charset="";
this.EnableViewState =false;
StringWriter tw=new StringWriter();
HtmlTextWriter wt =new HtmlTextWriter(tw);
string path=@"c:\test.htm";
//FileInfo info =new FileInfo(@"c:\test.txt");
StreamWriter wter = File.CreateText(path);
//输出所有信息
this.RenderControl(wt);
wter.Write(tw.ToString());
//Response.Write(tw.ToString());
wter.Close();
wt.Close();
tw.Close();
Response.End();
//生成相应文件,然后定向到该文件
//Response.Redirect(path);
------------------------------------------------------
输出aspx源码
------------------------------------------------------
string path = HttpContext.Current.Server.MapPath("");
Encoding code = Encoding.GetEncoding("gb2312");
// 读取模板文件
stringStreamReader sr=null;
StreamWriter sw=null;
string str="";
try
{
sr = new StreamReader(temp, code);
str = sr.ReadToEnd(); // 读取文件
this.TextBox1.Text = str.Trim();
}
catch(Exception exp)
{
HttpContext.Current.Response.Write(exp.Message);
HttpContext.Current.Response.End();
sr.Close();
}