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.

image

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:

5 Responses to “Use ICSharpCode.SharpZipLib to write directly to Response.OutputStream in a asp.net application”

  1. 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.

  2. 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.

  3. 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 :P. Thanks for comments.

    alk.

  4. 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

Trackbacks/Pingbacks

  1. Use ICSharpCode.SharpZipLib to write directly to Response … : insurancesitesfind