Click here to Skip to main content
Click here to Skip to main content

ZipStorer - A Pure C# Class to Store Files in Zip

By , 15 Mar 2010
 
Zip_Storer - Click on image to enlarge

Introduction

There are many techniques to produce Zip files in a .NET 2.0 environment, like the following:

  • Using the java.util.zip namespace
  • Invoking Shell API features
  • Using a third-party .NET library
  • Wrapping and marshalling a non-.NET library
  • Invoking a compression tool at command-line

I have tested most of them, each one has pros and cons, but sometimes I just needed a tiny library to store files in a Zip with basic compression or plain storing. I have built my own minimalistic class to create Zip files and store/retrieve files to/from it, firstly with uncompressed storing capabilities and now with Deflate algorithm. no other compression methods supported.

Moreover, notice that the new .NET 3.0 and 3.5 Frameworks come with the ZipPackage class, but it is not available for .NET 2.0 or Compact Framework applications. A restriction of ZipPackage is that you cannot avoid generating an extra file inside named [Content_Type].xml.

Background

The following diagram depicts a Zip file structure; you will notice it is a bit redundant because of its double directory approach (local and central). This is because it is designed to support creation in a sequential-access-only device.

Screenshot - Zip_Structure.png

The contents of each section can vary depending on the Operating System and hardware platform. The original PKWare specification has been included with this article.

Using the Code

The ZipStorer class is the unique one needed to create the zip file. It contains a nested structure (ZipFileEntry) to collect each directory entry. The class has been declared inside the System.IO namespace. The following diagram describes all the ZipStorer class members:

Class_Diagram.png

There is no default constructor. There are two ways to construct a new ZipStorer instance, depending on specific needs: use either Create() or Open() static methods. To create a new Zip file, use the Create() method like this:

ZipStorer zip = ZipStorer.Create(filename, comment);  // file-oriented version
ZipStorer zip = ZipStorer.Create(stream, comment);  // stream-oriented version

It is required to specify the full path for the new zip file, or pass a valid stream, and optionally add a comment. To open an existing zip file for appending, the Open() method is required, like the following:

ZipStorer zip = ZipStorer.Open(filename, fileaccess);  // file-oriented version
ZipStorer zip = ZipStorer.Open(stream, fileaccess);  // stream-oriented version

Where fileaccess should be of type System.IO.FileAccess enumeration type. Also, as now ZipStorer is derived from IDisposable interface, the using keyword can be used to ensure proper disposing of the storage resource:

using (ZipStorer zip = ZipStorer.Create(filename, comment))
{
    // some operations with zip object
    //
}   // automatic close operation here

To add files into an opened zip storage, there are two available methods:

public void AddFile(ZipStorer.Compress _method, string _pathname, string _filenameInZip,
string _comment);
public void AddStream(ZipStorer.Compress _method, string _filenameInZip, Stream _source,
    DateTime _modTime, string _comment);

The first method allows you to add an existing file to the storage. The first argument receives the compression method; it can be Store or Deflate enum values. The second argument admits the physical path name, the third one allows to change the path or file name to be stored in the Zip, and the last argument inserts a comment in the storage. Notice that the folder path in the _pathname argument is not saved in the Zip file. Use the _filenameInZip argument instead to specify the folder path and filename. It can be expressed with both slashes or backslashes.

The second method allows you to add data from any kind of stream object derived from the System.IO.Stream class. Internally, the first method opens a FileStream and calls the second method.

Finally, you have to close the storage with the Close() method. This will save the central directory information too. Alternatively, you can use Dispose() method.

Sample Application

The provided sample application will ask for files and store the path names in a ListBox, along with the operation type: creating or appending, and compression method. Once the Proceed button is pressed, the following code snippet will be executed:

ZipStorer zip;

if (this.RadioCreate.Checked)
    // Creates a new zip file
    zip = ZipStorer.Create(TextStorage.Text, "Generated by ZipStorer class");
    else
    // Creates a new zip file
    zip = ZipStorer.Open(TextStorage.Text, FileAccess.Write);

    // Stores all the files into the zip file
    foreach (string path in listBox1.Items)
    {
       zip.AddFile(this.checkCompress.Checked ? 
	ZipStorer.Compression.Deflate : ZipStorer.Compression.Store,
       	path, Path.GetFileName(path), "");
    }
}

// Creates a memory stream with text
MemoryStream readme = new MemoryStream(
System.Text.Encoding.UTF8.GetBytes(string.Format("{0}\r\nThis file
    has been {1} using the ZipStorer class, by Jaime Olivares.",
DateTime.Now, this.RadioCreate.Checked ? "created" : "appended")));

// Stores a new file directly from the stream
zip.AddStream("readme.txt", readme, DateTime.Now, "Please read");
readme.Close();

// Updates and closes the zip file
zip.Close();

This code snippet shows how to add both physical files and a little readme text from a memory stream.

Notice that the sample has been produced with Visual Studio 2008. The solution cannot be loaded directly with Visual Studio 2005, but a new solution can be created and the project file attached to it without problems.

Extracting Stored Files

To extract a file, the zip directory shall be read first, by using the ReadCentralDir() method, and then the ExtractStoredFile() method, like in the following minimal sample code:

// Open an existing zip file for reading
ZipStorer zip = ZipStorer.Open(@"c:\data\sample.zip", FileAccesss.Read);

// Read the central directory collection
List<ZipStorer.ZipFileEntry> dir = zip.ReadCentralDir();

// Look for the desired file
foreach (ZipStorer.ZipFileEntry entry in dir)
{
    if (Path.GetFileName(entry.FilenameInZip) == "sample.jpg")
    {
        // File found, extract it
        zip.ExtractStoredFile(entry, @"c:\data\sample.jpg");
        break;
    }
}
zip.Close();

Removal of Entries

Removal of entries in a zip file is a resource-consuming task. The simplest way is to copy all non-removed files into a new zip storage. The RemoveEntries() static method will do this exactly and will construct the ZipStorer object again. For the sake of efficiency, RemoveEntries() will accept many entry references in a single call, as in the following example:

List<ZipStorer.ZipFileEntry> removeList = new List<ZipStorer.ZipFileEntry>();

foreach (object sel in listBox4.SelectedItems)
{
    removeList.Add((ZipStorer.ZipFileEntry)sel);
}

ZipStorer.RemoveEntries(ref zip, removeList);

Files or Streams?

The current release of ZipStorer supports both files and streams for creating and opening a zip storage. Several methods are overloaded for this dual support. The advantage of file-oriented methods is simplicity, since those methods will open or create files internally. On the other hand, stream-oriented methods are more flexible by allowing to manage zip storages in streams different than files. File-oriented methods will invoke internally to equivalent stream-oriented methods. Notice that not all streams will apply, because the library requires the streams to be randomly accessed (CanSeek = true). The RemoveEntries method will work only if the zip storage is a file.

// File-oriented methods:
        public static ZipStorer Create(string _filename, string _comment)
        public static ZipStorer Open(string _filename, FileAccess _access)
        public void AddFile(Compression _method, 
		string _pathname, string _filenameInZip, string _comment)
        public bool ExtractFile(ZipFileEntry _zfe, string _filename)
        public static bool RemoveEntries
		(ref ZipStorer _zip, List<zipfileentry /> _zfes)  // No stream-oriented equivalent

// Stream-oriented methods:
        public static ZipStorer Create(Stream _stream, string _comment)
        public static ZipStorer Open(Stream _stream, FileAccess _access)
        public void AddStream(Compression _method, 
	string _filenameInZip, Stream _source, DateTime _modTime, string _comment)
        public bool ExtractFile(ZipFileEntry _zfe, Stream _stream)

Filename Encoding

Traditionally, the ZIP format supported DOS encoding system (a.k.a. IBM Code Page 437) for filenames in header records, which is a serious limitation for using non-occidental and even some occidental characters. Since 2007, the ZIP format specification was improved to support Unicode's UTF-8 encoding system.

ZipStorer class detects UTF-8 encoding by reading the proper flag in each file's header information. To enforce filenames to be encoded with UTF-8 system, set the EncodeUTF8 member of ZipStorer class to true. All new filenames added will be encoded with UTF8. Notice this doesn't affect stored file contents at all. Also be aware that Windows Explorer's embedded Zip format facility does not recognize well the UTF-8 encoding system, like it does WinZip or WinRAR.

Compatibility with ePUB & OCF

The ZipStorer library has been adjusted to comply with Open Container Format Specification (OCF), one of the standards required to produce ePUB Digital Books. There are some specific requirements to fulfill the OCF specification:

  • The storage shall have the .epub extension instead of .zip
  • The first file in storage must be non-compressed and shall be called mimetypes, containing the string application/epub+zip
  • Do not use comments in zip file entries or zip storage
  • The filenames shall be encoded in UTF8. Set the storage field EncodeUTF8 to true

Advantages and Usage

ZipStorer has the following advantages:

  • It is a short and monolithic C# class that can be embedded as source code in any project (1 source file of 33K, 700+ lines)
  • No external libraries, no extra DLLs in application deployments
  • No Interop calls, increments portability, maybe to Mono
  • Can also be implemented with .NET Compact Framework
  • Fast storing and extracting, because the code is simple and short
  • UTF8 Encoding support and ePUB compatibility

To implement this class into your own project, just add the ZipStorer.cs class file and start using it without any restriction. More recent updates can be found at my CodePlex page (zipstorer.codeplex.com).

History

  • November 23rd, 2007: First version
  • June 1st, 2008: Added append and extraction features
  • June 20th, 2008: Corrected some bugs in extraction portion
  • August 3rd, 2008: Corrected more bugs in extraction portion
  • October 3rd, 2008: Improved demo application with extraction code sample
  • August 22nd, 2009: Added compression capability
  • October 3rd, 2009: Added removal capability and other minor improvements
  • February 21st, 2010: Improved support to streams, and ePub compatibility
  • March 13th, 2010: Improved UTF-8 support and timestamp handling

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Jaime Olivares
Architect Freelance (jaimeolivares.com)
Peru Peru
Member


Computer Electronics professional, Software Architect and senior Windows C++ and C# developer with experience in many other programming languages, platforms and application areas including communications, simulation systems, GIS, 3D graphics and mobile platform.
Also have experience in development of electronic interfaces, specially for military applications.
Currently intensively working with Visual C# 2010 and TFS.
Can be reached at jaimeolivares.com

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberTejas Vaishnav1 Jan '13 - 0:48 
GeneralRe: My vote of 5memberStrange_Pirate1 Jan '13 - 1:08 
GeneralMy vote of 5memberAlexander Anding7 Nov '12 - 9:50 
GeneralMy vote of 5mvpKanasz Robert6 Nov '12 - 0:11 
GeneralMy vote of 5memberGregoryW26 Sep '12 - 19:35 
GeneralMy vote of 5memberMostafa M.A15 Aug '12 - 15:59 
BugSmall bug for compressed zero length files (empty files) [modified]memberSergei Petrik12 Feb '12 - 3:54 
QuestionMagnificentmemberCleveland Mark Blakemore30 Jan '12 - 14:37 
NewsZixFS base on ZipStorer, Big Thanks!!memberAzri Jamil2 Aug '11 - 5:08 
GeneralRe: ZixFS base on ZipStorer, Big Thanks!!memberJaime Olivares11 May '12 - 11:58 
GeneralMy vote of 5memberbipin99 Mar '11 - 19:37 
GeneralMy vote of 5memberAnderson Rancan24 Feb '11 - 1:47 
Generalusing under .net compact framework [modified]memberVasilyHohlov4 Jan '11 - 7:47 
QuestionHow to extract files to each archive folder?memberKohedlo5 Sep '10 - 21:18 
GeneralFrench char issuememberAjay Kale New31 Aug '10 - 18:07 
GeneralRe: French char issuememberJaime Olivares31 Aug '10 - 19:57 
Generalnice articememberMember 104674319 Jul '10 - 4:54 
GeneralNice articlemembersandeshmms13 Jul '10 - 20:57 
Nice article, I think this cmsstores.com may help some one who is looking for zip file using c# compressin
GeneralTanks a millionmemberfkniya1 Jul '10 - 4:15 
GeneralThanks!memberKarl Runmo6 Jun '10 - 23:04 
GeneralRe: Thanks!memberJaime Olivares7 Jun '10 - 3:41 
GeneralWindows, WinZip and others behaviormemberKazna4ey15 Mar '10 - 14:04 
GeneralThanks Jaime!memberHiAle12 Mar '10 - 16:34 
GeneralRe: Thanks Jaime!memberJaime Olivares13 Mar '10 - 13:44 
QuestionTimestampsmemberRBJensen4 Mar '10 - 2:58 
AnswerRe: TimestampsmemberJaime Olivares13 Mar '10 - 15:38 
GeneralRe: TimestampsmemberJaime Olivares15 Mar '10 - 12:38 
NewsMODIFICATION REQUIRED FOR UTF8 ENCODINGmemberJaime Olivares1 Mar '10 - 12:49 
GeneralRe: MODIFICATION REQUIRED FOR UTF8 ENCODINGmemberJaime Olivares13 Mar '10 - 18:48 
GeneralZipStorer - A Pure C# Class to Store Files in ZipmemberFregate1 Mar '10 - 12:25 
GeneralRe: ZipStorer - A Pure C# Class to Store Files in ZipmemberJaime Olivares1 Mar '10 - 12:34 
GeneralYou need to save UTF8 bit flag if UTF8 encoding is used [modified]memberKazna4ey25 Feb '10 - 12:51 
GeneralRe: You need to save UTF8 bit flag if UTF8 encoding is usedmemberJaime Olivares25 Feb '10 - 16:40 
GeneralRe: You need to save UTF8 bit flag if UTF8 encoding is usedmemberKazna4ey25 Feb '10 - 22:20 
GeneralRe: You need to save UTF8 bit flag if UTF8 encoding is usedmemberJaime Olivares15 Mar '10 - 12:39 
GeneralJust wanted to say thanks...memberMr Frumble13 Feb '10 - 4:50 
GeneralCompatibility with OCF Container formatmemberartwallacex20 Dec '09 - 21:00 
GeneralRe: Compatibility with OCF Container formatmemberJaime Olivares21 Dec '09 - 13:41 
GeneralRe: Compatibility with OCF Container formatmemberartwallacex21 Dec '09 - 19:32 
GeneralRe: Compatibility with OCF Container formatmemberJaime Olivares14 Feb '10 - 4:22 
GeneralRe: Compatibility with OCF Container formatmemberartwallacex14 Feb '10 - 17:05 
GeneralCyrillic letters supportmemberumaxsoft.com19 Nov '09 - 7:45 
GeneralRe: Cyrillic letters supportmemberJaime Olivares19 Nov '09 - 10:06 
GeneralRe: Cyrillic letters supportmemberJaime Olivares15 Mar '10 - 12:41 
Generalusage in a librarymemberSteffen Ploetz5 Nov '09 - 3:01 
GeneralRe: usage in a librarymemberJaime Olivares5 Nov '09 - 3:24 
GeneralRe: usage in a librarymemberJaime Olivares25 Feb '10 - 16:43 
Generala way to extract files to a memorystreammemberDrKnowLittle7 Oct '09 - 6:37 
GeneralRe: a way to extract files to a memorystreammemberJaime Olivares7 Oct '09 - 15:21 
GeneralRe: a way to extract files to a memorystreammemberJaime Olivares25 Feb '10 - 16:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 15 Mar 2010
Article Copyright 2007 by Jaime Olivares
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid