|
|
Comments and Discussions
|
|
 |
|

|
IIS support the compression and all browsers support it as well.
When you see your ViewState was originally 38Kb you got to remember that you see it AFTER decompression by the browser.
So the actual amount of data that was sent from IIS to the browser would be 17Kb (what you saw after your code ran).
Bottom line your code will only decrease performance since you doing compression twice on those 38Kb. First your code then IIS will do it.
But you are not saving any bandwidth.
Just my 2 cents.
George.
|
|
|
|

|
HTTP compression only works from the server to the client.
When you do a postback, you send data to the server (including the viewstate) uncompressed, so you are saving bandwidth.
|
|
|
|

|
I am getting same problem,
After using following code for compression,
using System.IO;
using System.IO.Compression;
///
/// Summary description for Compressor
///
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();
}
}
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));
}
When I click on next button my application gets stopped.
Please help me what should i do?
Regards,
Amit
|
|
|
|

|
Great article! Significant performance improvement! Simple, praticle and direct to the point! Congratz!
|
|
|
|

|
Why do you name the field "__VSTATE"?
Are there cases in which savings in viewstate size may become performance bottleneck?
Thanks.
|
|
|
|

|
It saves me a lot of time!
Thank you very!!!
Luisa
|
|
|
|
|

|
This is the result of the viewstate in my application:
Using your example:
H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/Iv7vX/M3/e1+zV/3F/z+v/9JtWzrqmxe5b9oXdT5y6ppn2TTt79Xfv37//6/za/56/7YyTyfvn1Svdv9TX/dH/v03sHuzt6Dg3uz/wcl/rgoPwAAAA=="
Without using your example, same webform:
="/wEPDwUJNjM4MTAyNzgzZBgBBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WAQUJQ2hlY2tCb3gxeC4oWoGK+hZjCjTZmpgxFgH6MxQ=
¿Am I doing something wrong?
|
|
|
|

|
I have the same result: my viewstate is bigger with compression than without.
|
|
|
|

|
I came across this article. It's a great article. Thanks for sharing.
I tried to play around with sample a bit. I found out that ViewState is really compressed if data is long enough. There are many times the size of ViewState data is grown after compression. For short data, the compression is not effeciently.
Here's my suggestions for optimizing codesnippets. First we insert a header (1-byte size) before the first byte of ViewState. This header tells whether ViewState data is compressed or not.
protected override void SavePageStateToPersistenceMedium(object viewState)
{
...
bytes = Compressor.Compress(bytes);
//NEW CODESNIPPET:
byte[] tmp = Compressor.Compress(bytes);
//If size is really smaller than the original one after compression, the compressed array is preferable
if (tmp.Length < bytes.Length)
{
byte[] tmp2 = new byte[tmp.Length + 1];
System.Buffer.BlockCopy(tmp, 0, tmp2, 1, tmp.Length);
tmp2[0] = 1; //Compressed. Save into the first byte
bytes = tmp2;
}
else
{
byte[] tmp2 = bytes;
byte[] tmp3 = new byte[bytes.Length + 1];
System.Buffer.BlockCopy(tmp2, 0, tmp3, 1, tmp2.Length);
tmp3[0] = 0; //Not Compressed. Save into the first byte
bytes = tmp3;
}
ClientScript.RegisterHiddenField("__VSTATE", Convert.ToBase64String(bytes));
}
protected override object LoadPageStateFromPersistenceMedium()
{
byte header = bytes[0]; //Extract data of the first byte. This data tells whether ViewState has been compressed or not previously
byte[] tmp = new byte[bytes.Length - 1];
System.Buffer.BlockCopy(bytes, 1, tmp, 0, bytes.Length - 1);
bytes = tmp;
if (header == 1)
bytes = Compressor.Decompress(bytes);
LosFormatter formatter = new LosFormatter();
return formatter.Deserialize(Convert.ToBase64String(bytes));
}
Is this codesnippet good?
|
|
|
|

|
First of all the default compression of gzip isn't very good. Using a more efficient compression method could eliminate this problem and have a higher compression ratio.
Second, your optimization would optimize for size but not for speed. My idea would be that instead of doing the actual compressing and then compare the size with the size of the uncompressed viewstate, the code would be faster to define a threshold that will generally mean that below that size threshold, compression isn't effective and should be skipped. so, if the size of the view state is for example smaller than 512 bytes then skip compression, otherwise compress it.
This way it would generally optimize for size without losing speed
PURPOSE: Delays program execution until designated condition is indicated.
|
|
|
|

|
Hi!!
(please excuse my english).
Thanks for the code, works 99% ok!
Also changed the ScriptManager.RegisterHiddenField like some poster adviced.
But i have some problems with usercontrols (.ascx) inside UpdatePanel.
My usercontrols have dropdown combos and i notice the the contains of the combos dissaperar.
Any advice?
Thanks in advance.
Best regards,
Homero Gonzalez.
|
|
|
|

|
Actually the Viewstate it's a great tool for Developing web App. but it can turn out to be a Performance trouble if it grows too much, your article it's a smooth workaround. Thanks for sharing it.
it also reduces the chance of getting the horrible MAC problem.
Great!
|
|
|
|

|
The magic number in GZip header is not correct. Make sure you are passing in a GZip stream
I got this error while in this line
read = gzip.Read(buff, 0, buff.Length);
pls help me out soon.
Maharajan,
software engineer,
Kom7 technologies,
India..
|
|
|
|

|
This is a great solution. I use the viewstate a lot, and even though I disable it where I can, I still get a viewstate larger than I would like.
Consider this modification:
public static class Compressor
{
public static byte[] Compress(byte[] data)
{
using (MemoryStream output = new MemoryStream())
{
using (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)
{
using (MemoryStream input = new MemoryStream())
{
input.Write(data, 0, data.Length);
input.Position = 0;
using(GZipStream gzip = new GZipStream(input, CompressionMode.Decompress, true))
{
using (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();
}
}
}
}
}
it's exactly the same as before, but since all the objects used are IDisposable, I've initiated them with "using()".
Here's why:
http://www.angrycoder.com/article.aspx?ArticleID=336[^]
I think there is a danger with the original code for memory leaks, because the objects are not disposed of.
What do you think?
|
|
|
|

|
Given that the streams I used here are always managed in RAM, I supposed that the use of using could be avoided because the objects are disposed at the end of their scope (I hope). At any rate, it's surely a good practice to use using.
|
|
|
|

|
Good Article! Great idea!.
I 've attempted to use this in an ASPNet 2.0 web application with AJAX 1.0 and found I get an ObjectDisposedException after selecting a grid row. I've used the correct ScriptManager reference for registering the hidden field. For some reason the stream is closed prematurely. The stack trace is
at System.IO.__Error.StreamIsClosed()
at System.IO.MemoryStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.Compression.DeflateStream.Read(Byte[] array, Int32 offset, Int32 count)
at System.IO.Compression.GZipStream.Read(Byte[] array, Int32 offset, Int32 count)
the error occurs in the DecompressViewState in the ViewStateCompressor.cs is
static byte[] DecompressViewState( byte[] compData)
{
.....
byteRead = gzip.Read(buf, 0, buf.Length);
.....
}
I'm trying to fathom the reason for the exception.
|
|
|
|

|
That's because you haven't taken into account "AsyncPostBacks". During every PostBack you need to Compress / Decompress the ViewState. This needs special handling during an AsyncPostBack where MS Ajax is involved. If you're using a differt Ajax Framework, sorry, I can't help you, but it's the same concept. You don't need to modify you Compress / Decompress routines, just the Restore/Save PageStateToPersistanceMedium.
Notice the bolded text in the "SavePageStateToPersistenceMedium" method. During an AsyncPostBack you need to re-register the HiddenField, however, you cannot simply just do this the same way. If you're in an AsyncPostBack, you need to use the ScriptManager methods to do this. If you're just in the first PageLoad or subsequent PostBacks, then using the ClientScript is fine.
protected override object LoadPageStateFromPersistenceMedium()
{
try {
string compression = ConfigurationManager.AppSettings["viewStateCompression"];
if (string.IsNullOrEmpty(compression)) compression = "true";
if (bool.Parse(compression)) {
string viewState = Request.Form["__VSTATE"];
if (viewState.EndsWith(",")) viewState = viewState.Substring(0, viewState.Length - 1);
byte[] bytes = Convert.FromBase64String(viewState);
bytes = BasePage.Decompress(bytes);
viewState = Convert.ToBase64String(bytes);
if (string.IsNullOrEmpty(viewState)) return null;
LosFormatter formatter = new LosFormatter();
return formatter.Deserialize(viewState);
}
else
return base.LoadPageStateFromPersistenceMedium();
}
catch (Exception) { throw; }
}
protected override void SavePageStateToPersistenceMedium(object state)
{
StringWriter writer = new StringWriter();
try {
string compression = ConfigurationManager.AppSettings["viewStateCompression"];
if (string.IsNullOrEmpty(compression)) compression = "true";
if (bool.Parse(compression)) {
LosFormatter formatter = new LosFormatter();
formatter.Serialize(writer, state);
string vState = writer.ToString();
byte[] bytes = Convert.FromBase64String(vState);
bytes = BasePage.Compress(bytes);
vState = Convert.ToBase64String(bytes);
System.Web.UI.ScriptManager sm = System.Web.UI.ScriptManager.GetCurrent(this);
if (sm != null && sm.IsInAsyncPostBack)
System.Web.UI.ScriptManager.RegisterHiddenField(this, "__VSTATE", vState);
else
Page.ClientScript.RegisterHiddenField("__VSTATE", vState);
}
else
base.SavePageStateToPersistenceMedium(state);
}
catch (Exception) { throw; }
finally {
if (writer != null) { writer.Dispose(); writer = null; }
}
}
|
|
|
|

|
You saved my Butt today, you code works perfectly, i didnt consider the ajax part..
Im using radcontrol to initiate ajax in my application.
From
M.Paige
|
|
|
|

|
Glad to help. I do have a small update though. There is no need to ever call Page.ClientScript if you have a ScriptManager because internally IT knows if it's within an AsyncPostBack and uses the ClientScript internally if needed, so you can change all code that does this:
System.Web.UI.ScriptManager sm = System.Web.UI.ScriptManager.GetCurrent(this);
if (sm != null && sm.IsInAsyncPostBack)
System.Web.UI.ScriptManager.RegisterHiddenField(this, "__VSTATE", vState);
else
Page.ClientScript.RegisterHiddenField("__VSTATE", vState);
To this (notice I am not checking if the current request is Async):
System.Web.UI.ScriptManager.RegisterHiddenField(...);
or
System.Web.UI.ScriptManager.RegisterScriptBlock(...);
or
System.Web.UI.ScriptManager.RegisterScript(...);
|
|
|
|

|
can this be done for the hidden field __EVENTVALIDATION also?
|
|
|
|

|
Any other known issues with this technic?
|
|
|
|

|
Hi, nice code.
I used it but i have some problems with it, (some controls loses the viewstate).
I found a better way apply the same idea:
Create a new class that inherits from PageStatePersister
and overrides the PageStatePersister property of the page.
here is the class i have made:
Public Class VSCompressor
Inherits PageStatePersister
Private _stateFormatter As LosFormatter
Protected Shadows ReadOnly Property StateFormatter() As LosFormatter
Get
If Me._stateFormatter Is Nothing Then
Me._stateFormatter = New LosFormatter
End If
Return Me._stateFormatter
End Get
End Property
Public Sub New(ByVal page As Page)
MyBase.New(page)
End Sub
Public Overrides Sub Load()
Dim bytes() As Byte = Convert.FromBase64String(MyBase.Page.Request.Form("__VSTATE"))
Dim input As New MemoryStream
input.Write(bytes, 0, bytes.Length)
input.Position = 0
Dim gzip As New GZipStream(input, CompressionMode.Decompress, True)
Dim output As New MemoryStream
Dim buff(64) As Byte
Dim read As Integer = -1
read = gzip.Read(buff, 0, buff.Length)
While read > 0
output.Write(buff, 0, read)
read = gzip.Read(buff, 0, buff.Length)
End While
gzip.Close()
gzip.Dispose()
Dim p As Pair = DirectCast(StateFormatter.Deserialize(Convert.ToBase64String(output.ToArray)), Pair)
MyBase.ViewState = p.First
MyBase.ControlState = p.Second
End Sub
Public Overrides Sub Save()
Dim writer As New StringWriter
StateFormatter.Serialize(writer, New Pair(MyBase.ViewState, MyBase.ControlState))
Dim bytes() As Byte = Convert.FromBase64String(writer.ToString)
Dim output As New MemoryStream
Dim gzip As New GZipStream(output, CompressionMode.Compress, True)
gzip.Write(bytes, 0, bytes.Length)
gzip.Close()
gzip.Dispose()
MyBase.Page.ClientScript.RegisterHiddenField("__VSTATE", Convert.ToBase64String(output.ToArray))
End Sub
End Class
this is the code you should put in the page:
Private _persister As New VSCompressor(Me)
Protected Overrides ReadOnly Property PageStatePersister() As PageStatePersister
Get
Return _persister
End Get
End Property
ing it the original size was 900kb
after compress it with your code was 300kb, and now is 150kb
|
|
|
|

|
Great solution. Worked perfectly, except to work with ajax postbacks please change the following line (as recommended by another poster):
MyBase.Page.ClientScript.RegisterHiddenField("__VSTATE", Convert.ToBase64String(output.ToArray))
With this:
ScriptManager.RegisterHiddenField(Page, "__VSTATE", Convert.ToBase64String(output.ToArray))
Otherwise, the viewstate doesn't get updated and can cause some very strange behaviour (as I learned the hard way!).
Cheers,
Mun
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
|
How to compress the ViewState of ASP.NET pages and save bandwidth.
| Type | Article |
| Licence | CPOL |
| First Posted | 10 Jul 2006 |
| Views | 184,794 |
| Bookmarked | 155 times |
|
|