Dynamically creating a file (in memory) and compressing it

Mar 13, 2009 at 9:20 AM

I use serialize to create 4 xml files, and I already have a method to export each one of them. I would like, however, to instead of exporting them to xml separate files, export them to a single zip file (a bit like Office 2007 files)..

Can this be done with DotNetZip library? if so, how?

Thanks in advance
Mar 13, 2009 at 7:40 PM
Edited Mar 22, 2009 at 5:55 AM
Hmm, what I would do is serialize each object to a MemoryStream, then use the MemoryStream as input to DotNetZip.

It might look like this:

    object[] thingsToSerialize = { Object1, Object2};

    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
    ns.Add( "", "");

    using (ZipFile zip = new ZipFile())
        int count= 1;
        foreach (object instance in thingsToSerialize)
            XmlSerializer s1 = new XmlSerializer(instance.GetType());
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            s1.Serialize(ms, instance, ns);
            ms.Seek(0, System.IO.SeekOrigin.Begin);

            string filename = String.Format("Config{0}.xml", count++);
            // this is called AddFileStream in v1.7 and prior
            zip.AddFileFromStream(filename, "", ms);

The AddFileFromStream call adds an entry to the zipfile, using the specified stream. In this case the stream is a memory stream that contains the XML that was serialized from the particular instance. Then the save is called just once - after all entries have been added. You can, of course, add other things into the zip - like a Readme file or whatever else. If you then unzip the resulting zip file, you will see a couple XML files.

Play around with the options and you can structure the XML file differently- with directories and a manifest if you like, a la Office 2007. You would have to create the manifest, your app would have to standardize ona format for the manifest and then validate the manifest against the actual content of the zip file, if that's what you're after.

You didn't say what language, so I wrote the example in C#. But you should be able to make sense of it even if you speak only VB.

In v1.8 you can use just-in-time streams. 

Mar 16, 2009 at 8:51 AM
Thanks for your input Cheeso,

So you mean it's possible, but only in the next version (1.8)... correct?
Mar 22, 2009 at 6:06 AM
Edited Mar 22, 2009 at 6:07 AM
Ah... no, sorry my original answer was not very clear.  I edited it and put the note about v1.8 at the end.  It seems clearer now.

You can use the code I showed above in v1.7.  It should just work.
In v1.8, there are some new possibilities with just-in-time streams.

"just in time" means you don't have to have all the streams for all the entries you add into the ZipFile, open at the same time.  Which is nice if you do lots and lots of files, but may not be so interesting if it is only 3 or 4. 

But the main reason I mentioned v1.8 is for code hygeine.
The code I showed above does not call Dispose() on the MemoryStream objects. For good hygeine, it *should*.   This good hygeine is also easier with the just-in-time streams, which is available only in v1.8.  

It works like this: 
call AddFileFromStream() with the name of your XML file, and a null value for the stream.  Also set the SaveProgress delegate.  When you later call zip.Save(), DotNetZip will invoke your SaveProgress delegate.  When the event type is BeforeWriteEntry, open and fill the MemoryStream (with the xml serializer) and set the ZipEntry.InputStream property.  After saving this entry DotNetZip will again invoke the SaveProgress event, with the event type AfterWriteEntry.  This is where you call Dispose() on the stream. 

This description in english is much less clear than actual code.  There's an example of what I am trying to describe, in the doc: http://cheeso.members.winisp.net/DotNetZipHelp/html/daf87dcb-ac4c-58c8-7f8b-0a03a7e586b4.htm