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

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

The main methods of the ZipReader class used to extract data from Zip archives are ReadItemLocalHeader and ReadItemData. The class also provides ByteProgression and InvalidPassword events to monitor the extraction operation. For details on the events used by Xceed Real-Time Zip for .NET / .NET CF, see Events

Xceed Real-Time Zip for .NET does not support reading split/spanned or self-extracting archives, or archives compressed inside another archive.

The Stored compression method and the None compression level are only supported on zip files that do not use the Zip64 format extensions designed to support zip items or zip archives larger than 4GB. Zip utilities (i.e., WinZip) will only use the Zip64 format extensions when necessary (with very large files that break the 4GB barrier). So even though these utilities will sometimes choose to store items rather than compress them, the ZipReader class should still be able to read these archives.

Using the ZipReader 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 extract items from 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

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

When extracting files from a Zip archive, a stream representing the source archive is first passed to the ZipReader constructor. A password can optionally be passed.

The data is then accessed by alternately calling ReadItemLocalHeader and ReadItemData until no more local headers are found (when null is returned by ReadItemLocalHeader). The ReadItemData method is very similar to the Read method of the Stream class: the data is retrieved into an array of bytes, with an offset indicating at which point to begin storing the data read from the Zip archive and a count indicating maximum number of bytes to be read.

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

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

Note that if the ZipReader encounters a ZipItemLocalHeader representing a folder, the header will not be returned by ReadItemLocalHeader. ZipWriter does not support writing folders as items in a Zip archive.

Make sure the Stream given to ZipReader is already positioned at the beginning of the zip archive. If the stream is seekable, a call to Stream.Seek( 0, SeekOrigin.Begin ) is usually sufficient

Example 1: Extracting data from a Zip archive using ZipReader

The following example shows how to read a Zip archive.

using System;
using System.IO;
using Xceed.Zip.ReaderWriter;
namespace RealTimeZipExamples
{
  class Examples
  {
    public static void TheZipReaderClass()
    {
      // Pick a zip file
      string zipFileName = @"D:\SomeFolder\SomeZipFile.zip";
      // Pick a base output folder, taking care not to end it with a directory separator
      string unzipToFolder = @"D:\SomeOtherFolder";
      // Open the source Zip file
      using( FileStream zipFileStream = new FileStream( zipFileName, FileMode.Open, FileAccess.Read ) )
      {
        // Create a Zip reader object around the stream.
        // Remember that ZipReader doesn't close the underlying stream given here
        using( ZipReader zipReader = new ZipReader( zipFileStream ) )
        {
          // Optional. Provide the default password for encrypted items in the archive
          zipReader.EncryptionPassword = "Password";
          // Optional. Subscribe to available events
          zipReader.ByteProgression += new EventHandler<ZipReaderByteProgressionEventArgs>( OnByteProgression );
          zipReader.InvalidPassword += new EventHandler<ZipReaderInvalidPasswordEventArgs>( OnInvalidPassword );
          ZipItemLocalHeader zipItemLocalHeader;
          // While the reader finds local headers
          while( ( zipItemLocalHeader = zipReader.ReadItemLocalHeader() ) != null )
          {
            // The 'FileName' property contains the sub-folders and filename
            string outputPath = unzipToFolder + zipItemLocalHeader.FileName;
            string outputFolder = Path.GetDirectoryName( outputPath );
            // Make sure the output folder exists
            Directory.CreateDirectory( outputFolder );
            // If the item isn't a folder entry
            if( !zipItemLocalHeader.IsFolder )
            {
              // Create/overwrite an output file using our calculated filename
              using( FileStream outputFileStream = new FileStream( outputPath, FileMode.Create, FileAccess.Write ) )
              {
                // Have the reader read the item data and write it to the stream using a 64K buffer
                zipReader.ReadItemData( outputFileStream, 64 * 1024 );
              }
            }
          }
          // Optional. Have the reader give us the zip ending header
          ZipEndHeader endHeader = zipReader.ReadEndHeader();
          // If the header contains a global zip comment
          if( endHeader != null && !String.IsNullOrEmpty( endHeader.ZipComment ) )
          {
            // TODO: Do something with the global zip comment
          }
        }
      }
    }
    private static void OnInvalidPassword( object sender, ZipReaderInvalidPasswordEventArgs e )
    {
      // TODO: We have access to the current item being unzipped. We can report it's name, etc
      ZipItemLocalHeader currentItem = e.ZipItemLocalHeader;
      // TODO: We're given the password that failed. We can report it, etc
      string oldPassword = e.OldPassword;
      /* If 'e.NewPassword' is set to null or an empty string, or 'e.Abort' is set to true,
      a ZipReaderException will be thrown for failure to decrypt the item.
      Since items can't be skipped, the entire unzip process will be canceled.
       
      When the event is triggered, 'e.NewPassword' is set to an empty string and 'e.Abort' is set
      to false. */
      // TODO: We have to supply a new password.
      // If that new password is invalid, the event will be triggered again
      e.NewPassword = "Some New Password";
      // TODO: We can ask the entire unzip operation to be aborted
      e.Abort = true;
    }
    private static void OnByteProgression( object sender, ZipReaderByteProgressionEventArgs e )
    {
      // TODO: We have access to the current item being unzipped. We can report it's name, etc
      ZipItemLocalHeader currentItem = e.ZipItemLocalHeader;
      // TODO: We're given the current amount of bytes unzipped for the item. We can report it, etc
      long bytesProcessed = e.BytesProcessed;
      /* Do not assume that e.UncompressedSize and e.Percent will contain useful values.
      Since ZipReader doesn't seek in the archive, it cannot always know the uncompressed
      size in advance. */
    }
  }
}
Imports System
Imports System.IO
Imports Xceed.Zip.ReaderWriter
Namespace RealTimeZipExamples
  Friend Class Examples
    Public Shared Sub TheZipReaderClass()
      ' Pick a zip file
      Dim zipFileName As String = "D:\SomeFolder\SomeZipFile.zip"
      ' Pick a base output folder, taking care not to end it with a directory separator
      Dim unzipToFolder As String = "D:\SomeOtherFolder"
      ' Open the source Zip file
      Using zipFileStream As New FileStream(zipFileName, FileMode.Open, FileAccess.Read)
        ' Create a Zip reader object around the stream.
        ' Remember that ZipReader doesn't close the underlying stream given here
        Using zipReader As New ZipReader(zipFileStream)
          ' Optional. Provide the default password for encrypted items in the archive
          zipReader.EncryptionPassword = "Password"
          ' Optional. Subscribe to available events
          AddHandler zipReader.ByteProgression, AddressOf OnByteProgression
          AddHandler zipReader.InvalidPassword, AddressOf OnInvalidPassword
          Dim zipItemLocalHeader As ZipItemLocalHeader
          ' While the reader finds local headers
          zipItemLocalHeader = zipReader.ReadItemLocalHeader()
          Do While zipItemLocalHeader IsNot Nothing
            ' The 'FileName' property contains the sub-folders and filename
            Dim outputPath As String = unzipToFolder & zipItemLocalHeader.FileName
            Dim outputFolder As String = Path.GetDirectoryName(outputPath)
            ' Make sure the output folder exists
            Directory.CreateDirectory(outputFolder)
            ' If the item isn't a folder entry
            If (Not zipItemLocalHeader.IsFolder) Then
              ' Create/overwrite an output file using our calculated filename
              Using outputFileStream As New FileStream(outputPath, FileMode.Create, FileAccess.Write)
                ' Have the reader read the item data and write it to the stream using a 64K buffer
                zipReader.ReadItemData(outputFileStream, 64 * 1024)
              End Using
            End If
              zipItemLocalHeader = zipReader.ReadItemLocalHeader()
          Loop
          ' Optional. Have the reader give us the zip ending header
          Dim endHeader As ZipEndHeader = zipReader.ReadEndHeader()
          ' If the header contains a global zip comment
          If endHeader IsNot Nothing AndAlso (Not String.IsNullOrEmpty(endHeader.ZipComment)) Then
            ' TODO: Do something with the global zip comment
          End If
        End Using
      End Using
    End Sub
    Private Shared Sub OnInvalidPassword(ByVal sender As Object, ByVal e As ZipReaderInvalidPasswordEventArgs)
      ' TODO: We have access to the current item being unzipped. We can report it's name, etc
      Dim currentItem As ZipItemLocalHeader = e.ZipItemLocalHeader
      ' TODO: We're given the password that failed. We can report it, etc
      Dim oldPassword As String = e.OldPassword
'      If 'e.NewPassword' is set to null or an empty string, or 'e.Abort' is set to true,
'      a ZipReaderException will be thrown for failure to decrypt the item.
'      Since items can't be skipped, the entire unzip process will be canceled.
'       
'      When the event is triggered, 'e.NewPassword' is set to an empty string and 'e.Abort' is set
'      to false.
      ' TODO: We have to supply a new password.
      ' If that new password is invalid, the event will be triggered again
      e.NewPassword = "Some New Password"
      ' TODO: We can ask the entire unzip operation to be aborted
      e.Abort = True
    End Sub
    Private Shared Sub OnByteProgression(ByVal sender As Object, ByVal e As ZipReaderByteProgressionEventArgs)
      ' TODO: We have access to the current item being unzipped. We can report it's name, etc
      Dim currentItem As ZipItemLocalHeader = e.ZipItemLocalHeader
      ' TODO: We're given the current amount of bytes unzipped for the item. We can report it, etc
      Dim bytesProcessed As Long = e.BytesProcessed
'      Do not assume that e.UncompressedSize and e.Percent will contain useful values.
'      Since ZipReader doesn't seek in the archive, it cannot always know the uncompressed
'      size in advance.
    End Sub
  End Class
End Namespace