I have a web site where I need to dynamically create rtf reports. I decided to use Asp.NEt 3.5 routing, (maybe I’ll discuss another day) so I can redirect request like
http://localhost/Myapp/reports/reportwizard/14/Report.zip
to an handler that dynamically creates the zip file and stream it to the client browser. Here is the code
Dim service As New WebLogic.Report.ReportService Dim generator As RTFSimpleDocGenerator = service.CreateReport(ReportId) context.Response.ContentType = "application/zip" Using ZipStream As ZipOutputStream = New ZipOutputStream(context.Response.OutputStream) Dim ZipEntry As ZipEntry = New ZipEntry("report.rtf") ZipStream.PutNextEntry(ZipEntry) ZipStream.SetLevel(5) generator.Save(ZipStream) End Using
As you can see the code is really simple, I create a ICSharpLib stream directly on context.Response.OutputStream, and then I simply use my rtf generator to write directly on the zipped stream, the result is.
What???? It seems that the ICSharpLib tries to send some wrong value to the write method. After about one hour spent trying to understand what is going wrong I was really puzzled. If I redirect the zip stream to a file all works fine, I obtain a perfectly valid zip file, but if I write directly to the response.OutputStream I have the error.
To solve this mistery I creates a simple wrapper stream that does nothing than redirect all the calls to the wrapped stream
Public Class mystream : Inherits System.IO.Stream Private wrapped As System.IO.Stream Public Sub New(ByVal wrapped As Stream) Me.wrapped = wrapped End Sub Public Overrides ReadOnly Property CanRead() As Boolean Get Return wrapped.CanRead End Get End Property ... Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer) wrapped.Write(buffer, offset, count) End Sub End Class
And discovered that the error occurred because write() gets called with a buffer with: lenght 0, offset 0 and count 0. This seems to me a perfectly reasonable call, unuseful, but reasonable. Checking with reflector you can find this code into the write method of the class HttpResponseStream
int num = buffer.Length - offset; if ((offset < 0) || (num <= 0)) { throw new ArgumentOutOfRangeException("offset"); }
So the httpResponseStream does not accepts zero bytes buffer, I wonder why. If you check the FileStream class you can verify that it accepts zero byte buffer array, this is the reason why saving the zip to a file works and saving directly to the response.outputstream gives you the error. The reason why HttpResponseStream does a similar check is a mistery, but I simply change my wrapper stream to be
Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer) If buffer.Length = 0 Then Return wrapped.Write(buffer, offset, count) End Sub
now my wrapper stream corrects the call avoiding to call the write method If the caller request for a zero byte write. Here is how I construct the zipstream using my wrapper to correct this situation
Using ZipStream As ZipOutputStream = New ZipOutputStream(New mystream(context.Response.OutputStream))
This makes everything works fine.
alk.
Tags: asp.net



March 9th, 2009 at 5:29 pm
You can use DotNetZip to save directly to the ASP.NET Response.OutputStream, with no intervening wrapper class.
No temp file is created – the zip content just goes straight out to the browser. It’s very simple to do.
A working example is here:
http://dinoch.dyndns.org:6060/.....sharp.aspx
You can see the source there, too.
DotNetZip is free and open source, a good library for zip files.
Check it out.
June 30th, 2009 at 3:54 am
But DotNetZip does not support streaming before the whole file is saved, whereas the sharpZip currently does, which is what alkampfer who posted this, was trying to do.
June 30th, 2009 at 12:42 pm
anyway ICSharpCode is in production and never gave me a problem. Actually im quite satisfied of it and I do not want to change it
. Thanks for comments.
alk.
July 2nd, 2009 at 10:22 am
I was able to get streaming with sharpZip, but not able to set the root directory, so I must physically copy the files before zipping, which is no good.
So I’m taking on Cheeso’s offer to use his DotNetZip and I’ll try to add support for realtime on-the-fly streaming output myself. If I succeed, I’ll tell you all. Moshe