Zip Your Streams with System.IO.Packaging
I've been reading about how to write data to disk using the System.IO.Packaging namespace in .NET 3.0. This lets you create "packages" which are essentially zip files. You can store whatever you like in 'em, and it all gets neatly wrapped up into a single file.
Comicster right now simply saves its DataSet (using the WriteXml method) through a CompressionStream to disk. That works quite well, and compresses very nicely (since it's XML). The overheads of using a DataSet do make the file slightly bigger than it needs to be, but my collection of over 700 comics clocks in at around 500kB, which is acceptable to me.
With the new .NET 3.0 version of Comicster that I've been toying with, I've more or less abandoned the DataSet, and instead I'm using raw objects. To stream them out to disk I'm using an XmlWriter and manually writing the bits that I want into a FileStream. Uncompressed, the resulting XML file is almost 2MB. Running this through a CompressionStream brings it back down to 290kB - well below the size of the DataSet's compressed file.
Using the System.IO.Packaging.Package class is only a few extra lines of code, and the resulting file is still only 291kB - an acceptable trade-off when you consider that I'm adhering to the zip file standard.
Basically a "package" is made up of "parts" (basically representing the files within the zip). Each "part" has a "GetStream()" method, and you can use your existing code to read to and write from that stream, so it's really easy to shoehorn into legacy code. Here's mine:
public static void Save(Collection collection, string filename) { string ext = Path.GetExtension(filename).ToLower(); if (ext == ".cmxx") { using (var pack = ZipPackage.Open(filename, FileMode.Create)) { var part = pack.CreatePart(_collectionUri, System.Net.Mime.MediaTypeNames.Text.Xml, CompressionOption.Maximum); Save(collection, part.GetStream()); pack.CreateRelationship(part.Uri, TargetMode.Internal, _packageRelationshipType); } } else // just save it as raw XML { using (var fileStream = new FileStream(filename, FileMode.Create)) { Save(collection, fileStream); } } }
Pretty cool, huh? All I've done is created a ZipPackage, created a single PackagePart within it and then used that part's GetStream method to give me a stream to save my collection to (using the Save method that I had already defined). Note that you can specify on a per-part basis what sort of compression you want to use. This is handy if you're saving already-compressed data (like jpegs) into the package.
The "_collectionUri" identifier is just a constant of type Uri with a value of "/Content/Collection.xml". That tells the zip file where to store that particular stream in the zip.
Considering how easy this, it's a no-brainer to use it if you're creating a .NET 3.0 application that needs to persist data to disk.
# Trackback from Alessandro Del Sole's Blog on 25/07/2007 7:38 AM
# Trackback from Jon Galloway on 25/10/2007 6:08 PM
# Trackback from Alessandro Del Sole's Blog on 27/10/2007 8:50 PM
Comments
# Othman Soufan
25/04/2007 8:09 PM
Do I have to add a System.Io.Packaging reference? each time I run the code I receive this messege "the type or namespace 'Packaging' does not exist in the namespace 'System.Io'", so what I have to do?
# mabster
25/04/2007 8:59 PM
Hi Othman,
I didn't have to add any references, but then I was doing this in a WPF application so it may have already had the correct assemblies in the project.
A quick look at the MSDN article for System.IO.Packaging.Package tells me it's in windowsbase.dll, so try adding a reference to that one.
# Captain Crash
16/05/2007 9:45 PM
Hi mebster, how could I use this with those filenames which has got some space character in their relative path, like "C:\My Documents"?
# mabster
17/05/2007 7:12 AM
Ahoy Captain,
I don't see why not! I don't think there's anything special about the filenames you use either for the individual "parts" or for the package itself.
# Captain Crash
18/05/2007 3:35 AM
If I use Package.CreatePart method with PackUriHelper.CreatePartUri to create correct Uri for parts, like:
PackagePart part = pack.CreatePart(PackUriHelper.CreatePartUri(new Uri("/Hello World/something.xml", UriKind.Relative)), System.Net.Mime.MediaTypeNames.Text.Xml, CompressionOption.Maximum)
then the part.Uri.OriginalString will be "/Hello%20World/something.xml". (Because of Open Packiging Conventions in the PackUriHelper.CreatePartUri method) And if I try to extract the package the path will be incorrect of these validation.
Because of this validation I think that I can't use this tool to store multiple data objects in a single container, even though the ZIP file is the primary physical format for the Package.
# kashif
26/09/2007 5:05 PM
Hi am new to this mabsterama, i ve got to store my pptx files in open xml format, hence i thought of doing it using system.io.packaging which ll zip the files and store it in open xml format, but am trying it from half a week but unable to get the success, and after storing in open xml format ie after zipping it up using system.io.packaging.package i ve to retrieve my slides back programitically, please help
# mabster
26/09/2007 5:31 PM
So you're writing a program that can open and save PowerPoint 2007 files?
Where are you stuck? I don't know anything about the internal XML format of .pptx files.
# kashif
26/09/2007 6:48 PM
Thanks for your reply, actually mabster am not getting a way to save the package, onee i open a package using system.io.package.open(filename)
by this i can open the package, but their is no save option to save the same
# mabster
26/09/2007 7:02 PM
There's no explicit "Save" method. Look at my code - my "Save" method (which isn't spelled out above, but is called a few times) just writes data to a Stream. That Stream comes from part.GetStream() ... so anything you write to it gets written into the part.
# kashif
26/09/2007 7:17 PM
thannx again, ya i ll try using that mabster , and i ll text you after trying that, please do text me if you find any way out of saving ppt files in xml and back from xml to ppt ,,"Programitically",,
# kashif
26/09/2007 11:38 PM
mabster i ve done it,, i did it in some other way and i ll be sending u the code,,
thank yo
# ganesh
27/09/2007 3:49 PM
Hi,
Can u just tel me how to unpack a docx file
extract its contents, and work through the unzipped files, and apply the XSLT to the xml files to convert dat files into a different xml files.
pls help me to solve this issue...
# kashif
27/09/2007 7:14 PM
i ve done the same with pptx files,, just you use a dll component, microsoft.office.interop.msword,,, and open the zip file by giving the path,, and save as docx ,, you can extract those
# kashif
28/09/2007 9:54 PM
hey mabster can you please help me how to change the template of pptx files programitically
# Thomas Steffes
10/04/2008 3:50 AM
Correct me if I'm wrong, but using System.IO.Packaging does not compress files, it just packages them into a .zip file?
# Jack Davis
10/02/2009 6:10 AM
The System.IO.Packaging PackagePart(package,partUri,contentType, compressionOption) constructor lets you specify if you want the file data compressed or not.