Reading streamed data from database to zip file

Oct 21, 2009 at 6:03 PM

I have been looking at your great library, but I do not know if it will be able to do this or do not see a way as yet.  I am reading chunks on binary data (actually very large XML document) from a SQL database as reading the data into a single Stream in one go will cause an Out of Memory error.  I have the chunking code for downloading to the browser ie.

 DataExportStreamer XMLData = new DataExportStreamer(DataExportReportID);
while (!XMLData.IsStreamEnd)
{
   if (XMLData.ReadDataSheetStream())
   {
      byte[] RawData = XMLData.XMLExcelSpreadsheet;
      Response.OutputStream.Write(RawData, 0, RawData.Length);
      Response.OutputStream.Flush();
      XMLDataRead = true;
   }
}

I want to now zip the data to a file location instead of streaming it to the browser,  Is there a way to do this?

 

 

 

 

 

 

Coordinator
Oct 21, 2009 at 7:58 PM
Edited Oct 21, 2009 at 9:06 PM

I don't understand what's special about your requirements.   Yes, you can zip the data to a file location.

What's the part you are stuck on?

If on the server side, you can open a stream that you can read from, then you can use the AddEntry() overload that accepts a stream as input.  When you call ZipFile.Save(), the stream is read, and compressed (and maybe encrypted) as it is streamed to the zip archive.  Check the code example on the doc page. 

String zipToCreate = "Content.zip";
String fileNameInArchive = "Content-From-Stream.bin";
using (System.IO.Stream streamToRead = MyStreamOpener())
{
  using (ZipFile zip = new ZipFile())
  {
    ZipEntry entry= zip.AddEntry(fileNameInArchive, streamToRead);
    zip.AddFile("Readme.txt");
    zip.Save(zipToCreate);  // the stream is read implicitly here
  }
}

 

 

Oct 22, 2009 at 10:53 AM

Thanks for your reply, When the zip.Save method is called, what methods and properties does the MyStreamOpener object have to support?  Do you have an example of a MyStreamOpener class? 

Coordinator
Oct 22, 2009 at 6:19 PM
Edited Oct 22, 2009 at 6:33 PM

In the example code, MyStreamOpener() is just a stand-in method - you replace that with whatever opens your stream.  It has to return a System.IO.Stream.  I thought you said you were reading chunks for a stream, which sounded to me, like you already had a stream.  In which case, you already have a method (I guess) that opens the stream.

Let's take a step back.

DotNetZip models the zip file as a container, in the ZipFile class.  This container holds entries, each represented by the ZipEntry class.  When extracting from a zip file (call ZipEntry.Extract() method), the normal way of operation is that each entry results in the creation of a file or directory in the filesystem.  When creating a zip file, you can create file entries from one of various sources.  The most obvious source is a filesystem file itself (ZipFile.AddFile()).  An alternative source is an arbitrary stream, via ZipFile.AddEntry(string name, Stream stream).  That's what I show in the example above.  

Another alternative is to create a ZipEntry using data stored in a byte array as the source.  (ZipFile.AddEntry(string name, byte[] byteContent) ).

Another alternative is to call the ZipFile.AddEntry() method that accepts a WriteDelegate.  With this option, the application (your code) writes the data into the ZipEntry, and the data need not come from a stream.  The key example here is saving a DataSet into a ZipEntry.

private void WriteEntry (String filename, Stream output)
{
    DataSet ds1 = ObtainDataSet();
    ds1.WriteXml(stream);
}

private void Run()
{
    Using zip = New ZipFile
    {
        zip.AddEntry(zipEntryName, WriteEntry);
        zip.Save(zipFileName);
    }
}

In this last approach, obviously the data does not have to come from a DataSet.  It can come from anywhere.  The point is, at the time of ZipFile.Save(), your code (via the WriteEntry method, or more accurately, any WriteDelegate) writes the entry content directly into the ZipFile.

You have to decide the best alternative for yourself. It depends on how your app accesses the entry content. Proabably the AddEntry() method that accepts a Stream or WriteDelegate is best for you.

Coordinator
Oct 22, 2009 at 7:21 PM

Stu, in looking again at your code, I don't see where the streaming is happening.

It seems to me you write a byte array out to the Response.OutputStream.  If that's the case, then all the data for the spreadsheet is in memory at a single time.  There is no streaming.

If you have all the data in memory, in a single byte array at one time, then the easiest method to save it into a zip file is to call the ZipFile.AddEntry() overload that accepts a byte array:

DataExportStreamer XMLData = new DataExportStreamer(DataExportReportID);
while (!XMLData.IsStreamEnd)
{
   if (XMLData.ReadDataSheetStream())
   {
      byte[] RawData = XMLData.XMLExcelSpreadsheet;
      using (var zip = new ZipFile())
      {
          zip.AddEntry("spreadsheetdata.xls", RawData);
          zip.Save("myArchive.zip");
      }
      XMLDataRead = true;
   }
}