Xceed .NET Libraries Documentation
The ZipWriter class
Welcome to Xceed .NET, .NET Standard and Xamarin Libraries! > Basic Concepts > Zip and streaming capabilities > Real-Time Zip Classes > The ZipWriter class

The ZipWriter class lets an application create a Zip archive without resorting to intermediate storage (either memory or disk). Furthermore, the creation of the Zip archive starts as soon as the ZipWriter object has data to compress: the resulting Zip archive is immediately available for processing, for example by an application using a ZipReader object receiving the archive over an FTP connection. The target Zip archive is passed to the ZipWriter's class constructor as a stream of any type.

The main methods of the ZipWriter class used to create Zip archives are WriteItemLocalHeader (in conjunction with a ZipItemLocalHeader object), WriteItemData, and CloseZipFile. The class also provides a ByteProgression event to monitor the progression of the Zip creation operation. For details on the events used by Xceed Real-Time Zip for .NET / .NET CF, see Events.

Using the ZipWriter Class

The following diagram will help to illustrate the relationship between the structure of a Zip archive and the class methods you must use to create a Zip archive.

Red boxes: Local header
Blue boxes: Compressed data
Green boxes: Data descriptor
Grey boxes: Central headers that make up the central directory

Interoperability vs. Size

The Zip file format can store item sizes and offsets in either 4-byte fields or 8-byte fields. This yields limits of 4GB and about 18 million terabytes (more precisely, 2^64 - 1 bytes), respectively. Using 8-byte fields is known as Zip64 extensions to the Zip file format. The original and most widely implemented specification, version 2.0, of the Zip format only supports 4-byte fields. Zip64 extensions were introduced in version 4.5 of the specification.

Most modern Zip utilities support Zip64 extensions seamlessly. But a few old or broken Zip implementations still do not. For the sake of interoperability, the ZipWriter class uses 4-byte fields by default. Zip64 extensions can still be enabled when constructing a ZipWriter instance.

This differs from previous versions of Xceed Real-Time Zip for .NET. Versions 4.2 and below always used Zip64 extensions. In more recent versions, Zip64 extensions are only used when explicitly enabled in the constructor.

An automatic mode where Zip64 extensions would be used when a 4GB+ item is detected isn't possible because that would cause the ZipWriter to seek in the target stream and correct some fields in a local header that's already been written.

WinRar, Zip tools on MacOS, and the built-in Zip support in Windows XP are known to lack support for the Zip64 extensions. So before creating a Zip archive, a decision must be made as to whether that archive will require Zip64 extensions or not. Here are the limits for the default behavior:

If these limits are likely to be broken or you want to be able to create Zip archives in every scenario, enable Zip64 extensions by setting the allowZip64Extensions parameter to true in the ZipWriter constructor.

For additional details concerning the format used for Zip archives, see the ZIP file format specification.

When creating a Zip archive, a stream representing the target archive is first passed to the ZipWriter constructor along with a boolean value indicating whether Zip64 extensions are to be used. A local header is then created for the first item to be archived and is written to the archive (red boxes in the diagram), followed by the item's data (blue boxes). This process is repeated until all of the items have been processed. Finally, the Zip archive is closed.

An item's local header is represented by a ZipItemLocalHeader object, which is written to the Zip archive using the WriteItemLocalHeader method. The encryption method/password, and compression level/method can optionally be specified when creating a ZipItemLocalHeader. See The ZipItemLocalHeader class for more details.

The current item's data is written by calling WriteItemData until all of its data has been written to the archive. The WriteItemData method is very similar to the Write method of the Stream class: the data to be written is passed to it as an array of bytes, with an offset indicating at what point to begin copying in the buffer and a count indicating the number of bytes to write from the offset.

There are several flavors of WriteItemData to accommodate different data scenarios and make efficient reuse of data buffers.

Another way to write data is to expose the item's data as a write-only Stream object with the GetItemDataStream method. Each call to the stream's Write method will call WriteItemData. This makes it possible to integrate ZipWriter with other classes that use the Stream class interface without the need for "glue code."

Once an item's data has been written, calling WriteItemLocalHeader with the header for the next item automatically causes the data descriptor (green boxes) of the previous item to be written.

Once the local headers and data of all the items have been written, CloseZipFile must be called to cause the data descriptor of the final item to be written, as well as the central directory (grey boxes) of the Zip archive.

The ZipWriter class writes all items with the UTF8Filename and UTF8Comment extra headers, but only if non-ASCII characters are used in the text. This corresponds to the UnicodeUsagePolicy.NonASCIIOnly value in Xceed Zip .NET.

Note that it doesn't write the old Xceed Unicode extra header, as it is deprecated.

Xceed Real-Time Zip for .NET does not support the creation of split/spanned or self-extracting archives

Example 1: Creating a Zip archive using ZipWriter

The following example demonstrates how to create a Zip archive locally using the files in a test directory.

using System.IO;
using Xceed.Zip.ReaderWriter;
//The target Zip archive
using (FileStream fileStream1 = new FileStream(@"d:\testOutput\test.zip",
  FileMode.Create, FileAccess.Write))
{
  //Create the ZipWriter object around the stream.
  Xceed.Zip.ReaderWriter.ZipWriter zipWriter1 =
    new Xceed.Zip.ReaderWriter.ZipWriter(fileStream1);
  //The source directory.
  DirectoryInfo directoryInfo = new DirectoryInfo(@"d:\test\");
  if (directoryInfo.Exists)
  {
    //Get files in the current directory and all subdirectories.
    FileInfo[] files = directoryInfo.GetFiles("*.*",
      SearchOption.AllDirectories);
    foreach (FileInfo file in files)
    {
      //Create ZipItemLocalHeader for current item and write to archive.
      ZipItemLocalHeader zipItemLocalHeader1 = new ZipItemLocalHeader
        (file.Name);
      zipWriter1.WriteItemLocalHeader(zipItemLocalHeader1);
      byte[] buffer = new byte[1024];
      int read = 0;
      using (FileStream fs = file.OpenRead())
      {
        //Read the current item's data
        while ((read = fs.Read(buffer, 0, buffer.Length)) != 0)
        {
          //Write the current item's data to the Zip archive
          zipWriter1.WriteItemData(buffer, 0, read);
          Console.WriteLine("Writing {0}. {1} bytes written.",
            zipItemLocalHeader1.FileName, read);
        }
      }
    }
    //Close the Zip archive. Writes the archive's central header.
    zipWriter1.CloseZipFile();
    Console.WriteLine("Zip archive created.");
  }
}
Imports System.IO
Imports Xceed.Zip.ReaderWriter
'The target Zip archive
Using fileStream1 As New FileStream("d:\testOutput\test.zip",
  FileMode.Create, FileAccess.Write)

  'Create the ZipWriter object around the stream.
  Dim zipWriter1 As New Xceed.Zip.ReaderWriter.ZipWriter(fileStream1)
  'The source directory
  Dim directoryInfo As New DirectoryInfo("d:\test\")
  If directoryInfo.Exists Then

    'Get files in the current directory and all subdirectories.
    Dim files As FileInfo() = directoryInfo.GetFiles("*.*",
      SearchOption.AllDirectories)

    For Each file As FileInfo In files
      'Create ZipItemLocalHeader for current item and write to archive.
      Dim zipItemLocalHeader1 As New ZipItemLocalHeader(file.Name)
      zipWriter1.WriteItemLocalHeader(zipItemLocalHeader1)
      Dim buffer As Byte() = New Byte(1023){}
      Dim read As Integer = 0
      Using fs As FileStream = file.OpenRead()
        'Read the current item's data
        read = fs.Read(buffer, 0, buffer.Length)
        Do While (read <> 0)
          'Write the current item's data to the Zip archive
          zipWriter1.WriteItemData(buffer, 0, read)
          Console.WriteLine("Writing {0}. {1} bytes written.", zipItemLocalHeader1.FileName, read)
          read = fs.Read(buffer, 0, buffer.Length)
        Loop
      End Using
    Next file
    'Close the Zip archive. Writes the archive's central header.
    zipWriter1.CloseZipFile()
    Console.WriteLine("Zip archive created.")
  End If
End Using