« On the Train to the Indigo SDR in Seattl... | Main | TechEd Video #3 - Drink the TechEd KoolA... »

Zipping/Compressing ViewState in ASP.NET

Posted 2005-03-29 05:26 PM in ASP.NET | DasBlog | ViewState | HttpModule | Bugs.

Here's an interesting, odd, but obvious idea. If you're not able to use HttpCompression like the Blowery HttpCompression module that we use with dasBlog due to the pile of bugs in older versions of IE around compression, you can "zip" up the ViewState on fat (usually DataGrid related bloat).

This is some VB code that Vlad Olifier did, that he said I could post on my blog. It's also a new submission at GotDotnet. To use it, you just derive your ASP.NET page class from it.

Using Zipped ViewState:

Public Class CompressPage
  Inherits PageViewStateZip

Just deriving from System.Web.UI.Page as usual:

Public Class RegularPage
  Inherits System.Web.UI.Page

The "trick" is pretty simple. There are little-known virtuals in Page that you can override - specifically LoadPageStateFromPersistanceMedium (where that persistance medium is a hidden input box) and SavePageStateToPersistenceMedium.

When it's time to load view state, we pull it out of the form and un-base64 the string into a byte array, un-zip the bytes, then deserialize the ViewState. When it's time to save, reverse the process - Serialize, zip, store. There's some overhead, certainly. The amount of compression is usually about 50%, but your mileage may vary (YMMV).

The real decision flow is this:

  • Can you use HttpCompression (via XCompress or an HttpModule)?
    • If so, use it.
  • Can't? (Bugs, SSL, Compatibility, bosses, don't want to take the perf hit, etc)
    • Got Fat ViewState on a few pages?
      • Use Zipped ViewState on a few pages as needed.
Imports System.IO
Imports Zip = ICSharpCode.SharpZipLib.Zip.Compression
 
Public Class PageViewStateZip : Inherits System.Web.UI.Page
  Protected Overrides Function LoadPageStateFromPersistenceMedium() As Object
    Dim vState As String = Me.Request.Form("__VSTATE")
    Dim bytes As Byte() = System.Convert.FromBase64String(vState)
    bytes = vioZip.Decompress(bytes)
    Dim format As New LosFormatter
    Return format.Deserialize(System.Convert.ToBase64String(bytes))
  End Function
 
  Protected Overrides Sub SavePageStateToPersistenceMedium(ByVal viewState As Object)
    Dim format As New LosFormatter
    Dim writer As New StringWriter
    format.Serialize(writer, viewState)
    Dim viewStateStr As String = writer.ToString()
    Dim bytes As Byte() = System.Convert.FromBase64String(viewStateStr)
    bytes = vioZip.Compress(bytes)
    Dim vStateStr As String = System.Convert.ToBase64String(bytes)
    RegisterHiddenField("__VSTATE", vStateStr)
  End Sub
End Class

Note that this sample uses SharpZipLib from ICSharpCode, but I assume you could use others, and I'd probably use System.IO.Compression if I was using .NET 2.0. The buffer sizes are hard-coded, but the only one that really matters it the one in Compress(). Again, salt to taste.

Imports System.IO
Imports Zip = ICSharpCode.SharpZipLib.Zip.Compression
'//--Download ICSharpCode.SharpZipLib from
'//--http://www.icsharpcode.net/OpenSource/SharpZipLib/Download.aspx
 
Public Class vioZip
  Shared Function Compress(ByVal bytes() As Byte) As Byte()
    Dim memory As New MemoryStream
    Dim stream = New Zip.Streams.DeflaterOutputStream(memory, _
                 New Zip.Deflater(Zip.Deflater.BEST_COMPRESSION), 131072)
    stream.Write(bytes, 0, bytes.Length)
    stream.Close()
    Return memory.ToArray()
  End Function
 
  Shared Function Decompress(ByVal bytes() As Byte) As Byte()
    Dim stream = New Zip.Streams.InflaterInputStream(New MemoryStream(bytes))
    Dim memory As New MemoryStream
    Dim writeData(4096) As Byte
    Dim size As Integer
    While True
      size = stream.Read(writeData, 0, writeData.Length)
      If size > 0 Then memory.Write(writeData, 0, size) Else Exit While
    End While
    stream.Close()
    Return memory.ToArray()
  End Function
End Class

Tracked by:
"What am I missing?" (sysadmin field notes) [Trackback]
".::Szőkelizer 142::." (RIO - Randektív Informatikai Oldal) [Trackback]
"Zip those fat ViewStates" (andrew connell) [Trackback]
"ViewState Compression and Persistence" (TuldokLambat) [Trackback]
"[导入]【笔记】关于ViewState截获移动到底部的问题" (junmy) [Trackback]


Wednesday, March 30, 2005 6:56:48 AM (Pacific Standard Time, UTC-08:00)
Nice... I use a similar technique in my PHP framework (which is actually ASP.NET based).

Store state:
base64_encode(gzdeflate(serialize($this->_state)))

Read state:
unserialize(gzinflate(base64_decode($postedState)))
Diego Mijelshon
Thursday, March 31, 2005 11:21:04 AM (Pacific Standard Time, UTC-08:00)
I use something similar but in a module to compress viewstate only on the fly (it's actually easier to just store it to a sql server and not send the viewstate at all, but that requires a server)

See http://philiprieck.com/blog/archive/2004/06/17/ViewRestate.aspx
Thursday, March 31, 2005 12:46:55 PM (Pacific Standard Time, UTC-08:00)
You can also keep viewstate on the server. Robert Boedingheimer did some work with this, and I also ran some interesting tests on different alternatives.

http://www.eggheadcafe.com/articles/20040613.asp
Tuesday, September 06, 2005 6:52:20 AM (Pacific Standard Time, UTC-08:00)
Storing ViewState on Server is not a bad idea but what if in case of a Webfarm Scenario ?
Compressed transmission is the only alternative in that case.
-Mohan
Mohan Deval
Wednesday, November 23, 2005 1:28:30 AM (Pacific Standard Time, UTC-08:00)
I like this article, so much that I port the code into .net 2.0 and additional feature in it. See my blogspot (http://tuldoklambat.blogspot.com)
Thursday, December 22, 2005 9:28:35 AM (Pacific Standard Time, UTC-08:00)
This is a nice solution. However, it can cause problems with request validation. Sometimes 'A potentially dangerous Request.Form' value is detected since the compressed viewstate can contain slashes. I attempted to use UrlEncode to fix this, but it causes invalid viewstate errors on loading.
Mark Petersen
Tuesday, January 17, 2006 12:20:54 PM (Pacific Standard Time, UTC-08:00)
C# 2.0 version I cobbled together

using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Compression;
using System.IO;

namespace YourNamespace.Web.Compression
{
public class ViewStateCompressor
{
static public byte[] Compress(byte[] buffer)
{
MemoryStream ms = new MemoryStream();

// Use the newly created memory stream for the compressed data.
GZipStream compressedzipStream = new GZipStream(ms, CompressionMode.Compress);

compressedzipStream.Write(buffer, 0, buffer.Length);

compressedzipStream.Close();

return ms.ToArray();
}

static public byte[] Inflate(byte[] buffer)
{
MemoryStream ms = new MemoryStream();
ms.Write(buffer, 0, buffer.Length);
ms.Seek(0, SeekOrigin.Begin);

GZipStream zipStream = new GZipStream(ms, CompressionMode.Decompress);

List&ltbyte> inflated = new List&ltbyte>();

while (true)
{
int b = zipStream.ReadByte();

if (b == -1)
break;

inflated.Add((byte)b);
}

return inflated.ToArray();
}
}
}


protected override object LoadPageStateFromPersistenceMedium()
{
string vState = Request.Form["__VSTATE"];
byte[] bytes = System.Convert.FromBase64String(vState);
bytes = ViewStateCompressor.Inflate(bytes);

LosFormatter format = new LosFormatter();
return format.Deserialize(System.Convert.ToBase64String(bytes));
}

protected override void SavePageStateToPersistenceMedium(object state)
{
LosFormatter format = new LosFormatter();
StringWriter writer = new StringWriter();

format.Serialize(writer, state);
string viewStateStr = writer.ToString();

byte[] bytes = System.Convert.FromBase64String(viewStateStr);

bytes = ViewStateCompressor.Compress(bytes);
string vStateStr = System.Convert.ToBase64String(bytes);
ClientScript.RegisterHiddenField("__VSTATE", vStateStr);
}
sbyard
Comments are closed.

Contact

Sponsors

Hosting By

Hot Topics

Tags

Calendar

<November 2009>
SunMonTueWedThuFriSat
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

Archives

November, 2009 (2)
October, 2009 (19)
September, 2009 (11)
August, 2009 (12)
July, 2009 (21)
June, 2009 (26)
May, 2009 (16)
April, 2009 (13)
March, 2009 (17)
February, 2009 (17)
January, 2009 (18)
December, 2008 (32)
November, 2008 (17)
October, 2008 (22)
September, 2008 (16)
August, 2008 (14)
July, 2008 (25)
June, 2008 (19)
May, 2008 (17)
April, 2008 (17)
March, 2008 (26)
February, 2008 (21)
January, 2008 (28)
December, 2007 (19)
November, 2007 (17)
October, 2007 (31)
September, 2007 (39)
August, 2007 (37)
July, 2007 (43)
June, 2007 (37)
May, 2007 (32)
April, 2007 (38)
March, 2007 (29)
February, 2007 (46)
January, 2007 (31)
December, 2006 (27)
November, 2006 (31)
October, 2006 (32)
September, 2006 (39)
August, 2006 (34)
July, 2006 (40)
June, 2006 (18)
May, 2006 (31)
April, 2006 (34)
March, 2006 (30)
February, 2006 (38)
January, 2006 (44)
December, 2005 (19)
November, 2005 (34)
October, 2005 (24)
September, 2005 (37)
August, 2005 (20)
July, 2005 (24)
June, 2005 (33)
May, 2005 (16)
April, 2005 (22)
March, 2005 (34)
February, 2005 (15)
January, 2005 (37)
December, 2004 (28)
November, 2004 (30)
October, 2004 (34)
September, 2004 (22)
August, 2004 (34)
July, 2004 (18)
June, 2004 (64)
May, 2004 (49)
April, 2004 (21)
March, 2004 (29)
February, 2004 (29)
January, 2004 (36)
December, 2003 (25)
November, 2003 (24)
October, 2003 (59)
September, 2003 (42)
August, 2003 (24)
July, 2003 (44)
June, 2003 (29)
May, 2003 (21)
April, 2003 (30)
March, 2003 (27)
February, 2003 (47)
January, 2003 (50)
December, 2002 (31)
November, 2002 (38)
October, 2002 (44)
September, 2002 (15)
May, 2002 (2)
April, 2002 (4)

Google Ads