Trying to zip from stream and then send to the client

May 26, 2010 at 6:54 PM

Hi, I have a code that generates XLS/CSV/XLSX file.
The code sends the file using ms.writeto(response.outputstream) to the client as a download dialog box.

I wanted to add a feature to download a compressed version of the file, so instead of sending the stream of the file, I zip it first and then send the stream of the zip file instead of the original XLS file.

I have been trying to do that but for some reason, using the code below, I get a ZIP file with an empty XLS file inside.

Please help me, I have been trying to solve it for few hours with no luck at all. 

Thanks,

Lior

Response.ContentType = "application/x-zip-compressed"

Response.AddHeader("Content-Disposition", "attachment; filename=" + reportName + ".zip")

Dim ms As MemoryStream = New MemoryStream

If FileFormat = "CSV" Then

ef.SaveCsv(ms, CsvType.CommaDelimited)

ElseIf FileFormat = "XLS" Then

ef.SaveXls(ms)

ElseIf FileFormat = "XLSX" Then

ef.SaveXlsx(ms)

End If

ef.SaveXlsx(ms)

Using zip As New ZipFile()

zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression

Dim entry As ZipEntry = zip.AddEntry("report.xls", ms)

entry.SetEntryTimes(Now(), Now(), Now())

zip.Save(Response.OutputStream)

End Using

Coordinator
May 27, 2010 at 2:34 AM

add

ms.Seek(0, SeekOrigin.Begin)

before you call

using zip as New ZipFile()

 

May 27, 2010 at 5:12 AM

Worked like a charm.

Thank you very much Cheeso. :)

Coordinator
May 27, 2010 at 11:34 AM

You could simplify the whole thing and eliminate the use of the temporary MemoryStream by using the WriteDelegate syntax of AddEntry. It was designed specifically for your situation.

Private Sub WriteEntry (ByVal filename As String, ByVal output As Stream) 
    If FileFormat = "CSV" Then
        ef.SaveCsv(stream, CsvType.CommaDelimited)
    ElseIf FileFormat = "XLS" Then
        ef.SaveXls(stream)
    ElseIf FileFormat = "XLSX" Then
        ef.SaveXlsx(stream)
    End If
End Sub

Public Sub Run()

    Response.ContentType = "application/x-zip-compressed"
    Response.AddHeader("Content-Disposition", "attachment; filename=" + reportName + ".zip")
    Using zip As New ZipFile()
        zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression
        Dim entry As ZipEntry = _
            zip.AddEntry("report.xls", New WriteDelegate(AddressOf WriteEntry))
        entry.SetEntryTimes(Now(), Now(), Now())
        zip.Save(Response.OutputStream)
    End Using

End Sub

But your original approach will work fine.

Jun 1, 2010 at 6:55 PM

I'm having a similar problem.  I use the following function to zip a set of files and return a MemoryStream to an Attachment object to be added to an Email object.  The zip file attaches OK but when I open it I find that it is empty.  If I Save() the zip file to a directory then it contains the files.  Any ideas?

        public Stream ZipFiles(string[] files)
        {
            MemoryStream stream = new MemoryStream();
            stream.Seek(0, SeekOrigin.Begin);
            using (ZipFile zip = new ZipFile())
            {
                foreach (String filename in files)
                {
                    ZipEntry e = zip.AddFile(filename);
                }              
                //zip.Save(@"test.zip");
                zip.Save(stream);
            }
            return stream;
        }

Thanks!

Ian

Coordinator
Jun 1, 2010 at 7:10 PM

yes - Your .Seek() should come AFTER you save the zipfile to the stream.

Jun 1, 2010 at 7:38 PM

That did it.  Thanks, Cheeso!  What threw me off was your first comment at the top of this thread.  I read it again and it still seems to me that it suggests putting the Seek() before the "using" which would also put it before the Save().  Hopefully this will help others from getting confused also.

Thanks for the great utility!

Ian

Coordinator
Jun 1, 2010 at 7:59 PM

Well in the situation at the top of the thread, the memorystream was the *source* of the data or a ZipEntry. 

In your case the MemoryStream was the *destination* of the ZipFile.

In either case, the situation was similar:  after writing to the MemoryStream, you must invoke MemoryStream.Seek() before trying to Read it.

  • In the first case, the Read() of the MemoryStream occured during ZipFile.Save(), therefore the MemoryStream.Seek() must be placed before ZipFile.Save(). 
  • In your case, the Read of the MemoryStream occurred after ZipFile.Save() wrote to the MemoryStream, therefore the MemoryStream.Seek() must be placed after ZipFile.Save().