HTML静态页面生成算法
使用HTML静态文档
几乎所有万维网网站都离不开数据库的支持!有了数据库技术,网络显得丰富多彩。不过,使用数据库是以消耗服务器资源为代价的。每查看刷新一次页面,就需要重新打开数据库读取数据。为了减少对数据库访问,提高网站运行效率,现在大多数网站的页面都采用一种静态的HTML文档!
难度所在
基于B/S结构,为一个动态的aspx文档生成静态的HTML文档,其难度在于无法获知在客户端浏览器中的HTML代码!就像你无法让服务器发出一条指令,通知客户端刷新浏览器一样。
服务器:论坛主题列表更新了...
客户端:啪,马上刷新了浏览器!
这样的情况是不会在B/S结构中实现的。对于命令请求,服务器是被动的!为什么会这样?为什么服务器端总是被动的?答案是这样可以减少服务器与浏览器间的依赖关系,也可以节省宝贵的网络资源(待到有客户端发出请求后,服务器再进行运算处理!而不是实时地检查客户端请求,就像Windows系统中的Idle进程那样。活来了就干,干完了就歇!而不是没事事!这就是服务器的性格)。
客户端:啪,刷新了浏览器(向服务器发出请求)...
服务器:接到请求,执行aspx页面。将最后的HTML代码发送给客户端。
问题就是这样,对于客户端,服务器是被动的,而对于服务器,客户端几乎是不可见的。所以,无法直接获取位于客户端浏览器中的代码,这给HTML生成算法造成了很大的难度。
传统的解决方式
我到网上过一些资料,大致看到了两种主要的解决方案。一种是使用HTML模板替换技术(说白了就是利用字符串覆盖),另一种是使用XmlHttpRequest技术(比较高级,代码也相对较少)。另外还有一些COM组件级的抓取工具类,使用起来比较方便。有兴趣的朋友,可以自己到网上搜索一下,这里就不详述了…
Bincess 的做法
彬月拥有自己的HTML静态页面生成技术,而且该技术未在其他网址看到过!下面就对其技术原理作以详细说明。因为一个aspx 页面在向客户端的浏览器发送HTML代码时,都是通过一个叫做Render的函数来完成的。
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
}
这个HtmlTextWriter类的对象writer就代表了客户端的浏览器的输出流。或者说,你可以认为它和StreamWriter一样!StreamWriter是将文本写入到磁盘上,而HtmlTextWriter是将文本写入到客户端浏览器上。如果你修改了该函数体,那么页面显示结果会发生变化。彬月论坛正式是通过对HtmlTextWriter类的继承和修改其行为来达到生成静态文档的目的的。
FetchHtmlWriter类
FetchHtmlWriter类实现了将aspx页面的HTML代码写入磁盘的操作。这个类实际上是“适配器模式”的一种应用。
第一步:派生HtmlTextWriter类
第二步:聚合StreamWriter类
第三步:聚合并适配HtmlTextWriter类
较为简单的示例代码如下:
// FetchHtmlWriter 类
public class FetchHtmlWriter : HtmlTextWriter网页设计html代码大全继承关系
{
// Html 输出流
private HtmlTextWriter htmlTextWriter=null;
// 文本数据流
private StreamWriter streamWriter=null;
/// <summary>
/// 类 CustomHtmlWriter 参数构造器
/// </summary>
/// <param name="htmlTextWriter"></param>
/// <param name="streamWriter"></param>
public FetchHtmlWriter(HtmlTextWriter htmlTextWriter, StreamWriter streamWriter) : base(htmlTextWriter) {
this.htmlTextWriter=htmlTextWriter;
this.streamWriter=streamWriter;
}
}
接下来要做的就是覆写HtmlTextWriter中的所有虚函数!这个是最累的... 函数形式就类似于下面的代码:
public override void Write(string value)
{
// 先将字符串送入浏览器输出
this.htmlTextWriter.Write(value);
// 再将字符串送入文件输出流,输出
this.streamWriter.Write(value);
}
详细代码,请参见Bincess/InterService/FetchHtmlWriter.cs文件
回到Index.aspx文件
如果要抓取Index.aspx文件所生成的Html代码,那么可以覆写其Render方法:
/// <summary>
/// 覆写 Render 函数,抓取页面所呈现的 Html 代码
/// </summary>
/// <param name="writer"></param>
protected override void Render(HtmlTextWriter writer)
{
// 写磁盘I/O流
StreamWriter streamWriter=new
StreamWriter(Server.MapPath("Index.html"), false, System.Text.Encoding.UTF8);
base.Render(new FetchHtmlWriter(writer, streamWriter));
streamWriter.Flush();
streamWriter.Close();
}
彬月的做法是在最开始的时候,拦截HtmlTextWriter类对象writer,并作重新进行适配。适配成一种FetchHtmlWriter 类对象,这样我们即做到了抓取页面的HTML代码的工作。
Render参数的一些说明
可能你会奇怪,那个最开始在Render函数中的writer参数是怎么一回事?它的工作原理是什么?对于初学者来说,这个的确有些复杂,难以解释。不知道你有没有听说过:“故事接力”的游戏?这个游戏就是先有人在纸上写个开头,然后每个人都在前一个人写的话后面再写一句话,把故事编完。例如:
第一个人写到:“很久很久以前,有一个人被困在大沙漠里”。
第二个人接到:“他在沙漠里四处寻水源”。
第三个人接到:“走着,走着,忽然发现一个神灯?”。
第四个人接到:“他拾起这盏灯用力挫,希望能象神话传说那样出现灯神,好带他离开这片死海…”。
第五个人…
这个HtmlTextWriter类对象writer,就是那张写满故事的纸!而一个页面上的每一个标记,就都是一个个的接力者。当纸条传给一个接力者时,这个接力者就必须把自己想好的情节写在后面,否则故事就中断了。Page类的派生类,它倡起了这个接力游戏,并把最开始的情节写在writer上。然后它就把writer传递给了自己的子控件,让子控件来继续往writer中填写情节,子控件写完之后,又将writer传递给了下一个控件。直到完成这个故事情节,即一个完整的页面。
HTML静态页面生成流程
当一个主题被浏览的时候,就生成其静态页面!而不是主题发送完成后马上为其建立静态页面。这是彬月的静态页面生成流程。那么接踵而至的一个问题就是:如果这个主题有新的回复了,那么怎么将回复插入到主题之后呢?将新回复插入到数据库是一定的了,还要将回复的HTML代码插入静态文件中。这个问题就难办了...因为,对于新的插入位置,并不好计算。假如是以XML文档来记录主题页的话,那么插入新的回复,直接就可以利用Xml名称空间来完成,比较可靠。彬月论坛并没有使用Xml文档,但也解决了回复主题的问题。彬月的做法比较精巧...
当用户查看主题时,便生成静态文档。而当用户回复主题之后,静态文档被删除了...
当主题再次被浏览时(回到流程图1),因为主题不存在,所以又必须重新生成静态文档。流程非常简单,但却非常有效!这样的做法是否出乎你的意料呢?

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。