序列化并压缩视图状态

介绍

近来我开发了一个巨大的APS.NET页面,多达30个控件.我们知道,我们可以禁用一些我们实际不需要的控件的视图状态,这不失为一个好主意,比如Literal和Label.在这之后,我发现隐藏的视图状态仍有好几KB之巨.这对那些没有宽带上网的用户来说很明显是一个大问题,因为要上传40KB的东西到服务器是一个很坏的情况,更有可能是在用户没有收到任何回应时一次又一次的点击”提交”.所以,我在网上搜索了一下,建立了一个简单的解决方案来压缩视图状态,这样可以节约50%的带宽. Scott Hanselman的这篇文章 被认为是写的很有用的.虽然可以用外部的一些类库完成压缩任务,但我想还是用.NET Framework 2.0自带的GZipStream或DeflateStream.

在内存中压缩/解压数据

首先,我们要找到一种方法可以在内存中压缩/解压字节数组.我把简单静态类中暴露的两种方法放在一起: Compress和Decompress.两个可用的类, GZipStream 和 DeflateStream,通过MSDN我们知道使用的是一样的算法,所以随便你选哪种都没有关系.

以下代码很简单,就不用再解释什么了:

using  System.IO;
using  System.IO.Compression;

public   static   class  Compressor {

  
public   static   byte [] Compress( byte [] data) {
    MemoryStream output 
=   new  MemoryStream();
    GZipStream gzip 
=   new  GZipStream(output, 
                      CompressionMode.Compress, 
true );
    gzip.Write(data, 
0 , data.Length);
    gzip.Close();
    
return  output.ToArray();
  }

  
public   static   byte [] Decompress( byte [] data) {
    MemoryStream input 
=   new  MemoryStream();
    input.Write(data, 
0 , data.Length);
    input.Position 
=   0 ;
    GZipStream gzip 
=   new  GZipStream(input, 
                      CompressionMode.Decompress, 
true );
    MemoryStream output 
=   new  MemoryStream();
    
byte [] buff  =   new   byte [ 64 ];
    
int  read  =   - 1 ;
    read 
=  gzip.Read(buff,  0 , buff.Length);
    
while (read  >   0 ) {
      output.Write(buff, 
0 , read);
      read 
=  gzip.Read(buff,  0 , buff.Length);
    }
    gzip.Close();
    
return  output.ToArray();
  }
}

 

 

你要把这个类保存为CS文件然后放在你ASP.NET应用程序的App_Code目录中,确认它包含正确定制的命名空间(如果你没有指定命名空间,这个类将在内建的ASP命名空间内可用.

压缩视图状态

现在我们可以开始真正压缩页面的视图状态了.要完成这个目标,我们必须重写两个方法LoadPageStateFromPersistenceMedium 和 SavePageStateToPersistenceMedium.以下代码简单地使用了附加的隐藏域, __VSTATE,来存放压缩后的视图状态.正如你看到的,通过观察页面的HTML, __VIEWSTATE域为空,而我们的__VIEWSTATE域包含压缩的视图状态,已编码成Base64.让我们来看看代码:

public  partial  class  MyPage : System.Web.UI.Page {

  
protected   override   object  LoadPageStateFromPersistenceMedium() {
    
string  viewState  =  Request.Form[ " __VSTATE " ];
    
byte [] bytes  =  Convert.FromBase64String(viewState);
    bytes 
=  Compressor.Decompress(bytes);
    LosFormatter formatter 
=   new  LosFormatter();
    
return  formatter.Deserialize(Convert.ToBase64String(bytes));
  }

  
protected   override   void  SavePageStateToPersistenceMedium( object  viewState) {
    LosFormatter formatter 
=   new  LosFormatter();
    StringWriter writer 
=   new  StringWriter();
    formatter.Serialize(writer, viewState);
    
string  viewStateString  =  writer.ToString();
    
byte [] bytes  =  Convert.FromBase64String(viewStateString);
    bytes 
=  Compressor.Compress(bytes);
    ClientScript.RegisterHiddenField(
" __VSTATE " , Convert.ToBase64String(bytes));
  }

  
//  The rest of your code here
}

在第一个方法中,我们只是从Base64解码,解压,再反序列化__VSTAT中的内容,再交给运行时.在第二个方法中我们执行相反的操作:序列化,压缩,然后编码成Base64. Base64字符串接着被保存到__VSTAT隐藏域中. LosFormatter对象执行序列化和反序列化任务.

可能你要建立一个新类,比如, CompressedPage,从System.Web.UI.Page继承,这样你可以重写这两个方法而且让你自己的页面继承自这个类,比如MyPage : CompressedPage.要记住.NET只有单继承,而且通过这种方式,你把唯一的继承机会用在了视图状态压缩上.另一方面来讲,在每一个类中重写这两个方法是浪费你时间的,所以你要找个最合适你的方法.

性能与总结

在做了一些测试之后,我注意到视图状态已从38K下降到17KB,节约了44%.假设平均每用户每分钟向你回送一次,你就可以在一个月内为单个用户节约

我要指出的是这个解决方案的性能参考(译者:原文是performance hit,我觉得大概可以这么理解,欢迎讨论)也要取决于服务器的硬件.压缩,解压,编码和解码都会对服务器有工作负担,所以你要通过CPU处理能力和内存来权衡用户数量.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值