Xceed Real-Time Zip for .NET Documentation
Reading and writing nested Zip archives - Snippet

The following examples show how to use the ZipReader.GetItemDataStream and ZipWriter.GetItemDataStream methods to allow the component to easily handle nested zip archives.

A nested zip archive is when an item in an archive is another zip archive. By providing the stream returned by the method to a new instance of ZipWriter or ZipReader, a nested zip file will be created or read.

The first example shows how the ZipWriter.GetItemDataStream() method can be used to create a nested Zip archive.

static void NestedArchiveExample( Stream someWriteStream )

{

  string password = "password";



  // Create the ZipWriter object around the supplied stream

  using( ZipWriter writer = new ZipWriter( someWriteStream ) )

  {

    /* OPTIONAL, BUT USEFUL: Allow uncompressed items in the archive. This will allow nested

     * zip files to be accessed and read without first having to unzip the entire rested zip file

     * from the 'outer' archive. */

    

    // Enable the use of uncompressed data in the archive

    writer.AllowUncompressedData = true;



    /* Create a local header object that will be used to specify the path/filename in

       the zip file and other options like compression and encryption. */

    ZipItemLocalHeader header = new ZipItemLocalHeader();



    // Setup a normal file for zipping

    header.FileName = "File1.dat";

    header.CompressionMethod = Xceed.Compression.CompressionMethod.Deflated;

    header.CompressionLevel = Xceed.Compression.CompressionLevel.Normal;

    header.EncryptionMethod = Xceed.Zip.EncryptionMethod.WinZipAes;

    header.EncryptionPassword = password;

    

    // Write the local header for the item

    writer.WriteItemLocalHeader( header );

    

    // Open the file's data

    using( Stream dataStream = new FileStream( @"C:\Windows\Notepad.exe", FileMode.Open, FileAccess.Read ) )

    {

      // Zip the data into the archive

      writer.WriteItemData( dataStream );

    }



    /* Now, we will add a nested zip file in the archive. For easy unzipping later,

     * the best approach is to add the nested zip file without compression or encryption.

     * 

     * This way, when unzipping, the nested zip file can be accessed directly with seeking

     * enabled.

     * 

     * If compression or encryption is enabled, the nested zip file will have to be

     * unzipped from the outer zip file into a temporary location before it can be itself unzipped.

     * 

     * The nested zip file itself can, of course, use compression and encryption for its items

     * without issue. So there is no loss of size and security with this approach.

     */



    // Assign a filename for the nested zip archive

    header.FileName = "File1.zip";



    // Disable compression and encryption for the nested zip archive

    header.CompressionMethod = Xceed.Compression.CompressionMethod.Stored;

    header.EncryptionPassword = String.Empty;



    // Write the local header for the file in the archive

    writer.WriteItemLocalHeader( header );



    // Create a write stream that wraps the item's data

    using( Stream itemStream = writer.GetItemDataStream() )

    {

      // Feed the item stream to a new instance of ZipWriter

      using( ZipWriter nestedWriter = new ZipWriter( itemStream ) )

      {

        /* The 'using' statement will insure the nested zip file is properly terminated */



        // Add an item and some data into the nested zip file

        header.FileName = @"\SomeFolder\File1.dat";

        header.CompressionMethod = Xceed.Compression.CompressionMethod.Deflated;

        header.CompressionLevel = Xceed.Compression.CompressionLevel.Normal;

        header.EncryptionMethod = Xceed.Zip.EncryptionMethod.WinZipAes;

        header.EncryptionPassword = password;

        nestedWriter.WriteItemLocalHeader( header );



        // Open the file's data

        using( Stream dataStream = new FileStream( @"C:\Windows\Notepad.exe", FileMode.Open, FileAccess.Read ) )

        {

          // Zip the data into the archive

          nestedWriter.WriteItemData( dataStream );

        }



        /* TODO: More items can be added to the nested archive this way... */

      }

    }



    // Setup a normal file for zipping

    header.FileName = "File34.dat";

    header.CompressionMethod = Xceed.Compression.CompressionMethod.Deflated;

    header.CompressionLevel = Xceed.Compression.CompressionLevel.Normal;

    header.EncryptionMethod = Xceed.Zip.EncryptionMethod.WinZipAes;

    header.EncryptionPassword = password;



    // Write the local header for the item

    writer.WriteItemLocalHeader( header );



    // Open the file's data

    using( Stream dataStream = new FileStream( @"C:\Windows\Notepad.exe", FileMode.Open, FileAccess.Read ) )

    {

      // Zip the data into the archive

      writer.WriteItemData( dataStream );

    }

  }

}
Private Shared Sub NestedArchiveExample(ByVal someWriteStream As Stream)

  Dim password As String = "password"



  ' Create the ZipWriter object around the supplied stream

  Using writer As New ZipWriter(someWriteStream)

    '         OPTIONAL, BUT USEFUL: Allow uncompressed items in the archive. This will allow nested

    '         * zip files to be accessed and read without first having to unzip the entire rested zip file

    '         * from the 'outer' archive. 



    ' Enable the use of uncompressed data in the archive

    writer.AllowUncompressedData = True



    '         Create a local header object that will be used to specify the path/filename in

    '           the zip file and other options like compression and encryption. 

    Dim header As New ZipItemLocalHeader()



    ' Setup a normal file for zipping

    header.FileName = "File1.dat"

    header.CompressionMethod = Xceed.Compression.CompressionMethod.Deflated

    header.CompressionLevel = Xceed.Compression.CompressionLevel.Normal

    header.EncryptionMethod = Xceed.Zip.EncryptionMethod.WinZipAes

    header.EncryptionPassword = password



    ' Write the local header for the item

    writer.WriteItemLocalHeader(header)



    ' Open the file's data

    Using dataStream As Stream = New FileStream("C:\Windows\Notepad.exe", FileMode.Open, FileAccess.Read)

      ' Zip the data into the archive

      writer.WriteItemData(dataStream)

    End Using



    '         Now, we will add a nested zip file in the archive. For easy unzipping later,

    '         * the best approach is to add the nested zip file without compression or encryption.

    '         * 

    '         * This way, when unzipping, the nested zip file can be accessed directly with seeking

    '         * enabled.

    '         * 

    '         * If compression or encryption is enabled, the nested zip file will have to be

    '         * unzipped from the outer zip file into a temporary location before it can be itself unzipped.

    '         * 

    '         * The nested zip file itself can, of course, use compression and encryption for its items

    '         * without issue. So there is no loss of size and security with this approach.

    '         



    ' Assign a filename for the nested zip archive

    header.FileName = "File1.zip"



    ' Disable compression and encryption for the nested zip archive

    header.CompressionMethod = Xceed.Compression.CompressionMethod.Stored

    header.EncryptionPassword = String.Empty



    ' Write the local header for the file in the archive

    writer.WriteItemLocalHeader(header)



    ' Create a write stream that wraps the item's data

    Using itemStream As Stream = writer.GetItemDataStream()

      ' Feed the item stream to a new instance of ZipWriter

      Using nestedWriter As New ZipWriter(itemStream)

        ' The 'using' statement will insure the nested zip file is properly terminated 



        ' Add an item and some data into the nested zip file

        header.FileName = "\SomeFolder\File1.dat"

        header.CompressionMethod = Xceed.Compression.CompressionMethod.Deflated

        header.CompressionLevel = Xceed.Compression.CompressionLevel.Normal

        header.EncryptionMethod = Xceed.Zip.EncryptionMethod.WinZipAes

        header.EncryptionPassword = password

        nestedWriter.WriteItemLocalHeader(header)



        ' Open the file's data

        Using dataStream As Stream = New FileStream("C:\Windows\Notepad.exe", FileMode.Open, FileAccess.Read)

          ' Zip the data into the archive

          nestedWriter.WriteItemData(dataStream)

        End Using



        ' TODO: More items can be added to the nested archive this way... 

      End Using

    End Using



    ' Setup a normal file for zipping

    header.FileName = "File34.dat"

    header.CompressionMethod = Xceed.Compression.CompressionMethod.Deflated

    header.CompressionLevel = Xceed.Compression.CompressionLevel.Normal

    header.EncryptionMethod = Xceed.Zip.EncryptionMethod.WinZipAes

    header.EncryptionPassword = password



    ' Write the local header for the item

    writer.WriteItemLocalHeader(header)



    ' Open the file's data

    Using dataStream As Stream = New FileStream("C:\Windows\Notepad.exe", FileMode.Open, FileAccess.Read)

      ' Zip the data into the archive

      writer.WriteItemData(dataStream)

    End Using

  End Using

End Sub

The second example shows how the ZipReader.GetItemDataStream method can be used to read a nested Zip archive.

private void ReadZipArchive( Stream archiveStream )

{

  // Create the ZipReader object around the stream

  using( ZipReader reader = new ZipReader( archiveStream ) )

  {

    ZipItemLocalHeader header;



    // While the reader finds local headers

    while( ( header = reader.ReadItemLocalHeader() ) != null )

    {

      /* The component will not automatically identity what is and is

       * not a nested zip file. The application needs to have its own mechanism.

       *

       * This example will keep it simple and just look at the item's file name extension. */



      // If the item's extension is .zip

      if( StringComparer.OrdinalIgnoreCase.Compare( Path.GetExtension( header.FileName ), ".zip" ) == 0 )

      {

        // Create a read stream that wraps the item's data

        using( Stream itemStream = reader.GetItemDataStream() )

        {

          /* NOTE: While using a recursive call here makes for elegant and compact code that

           * helps illustrate the concept of nested zip archives, a maliciously crafted zip file

           * made up of a large number of nested zip files could make this code cause a stack overflow

           * due to excessive recursion. */



          // Call ourselves to read the nested archive

          this.ReadZipArchive( itemStream );

        }



        /* IMPORTANT: Before we can move on to the next item in the archive, ZipReader must have

         * reached the end of the current item's data. Reading a nested zip file does not ensure

         * this, so we need to make sure here by reading any remaining data into a dummy stream.

         *

         * Failure to do this will result in a ZipReaderException that reports that the object

         * is not in the correct state to read the next item header. */



        // Make sure we reach the end of the item's data

        reader.ReadItemData( Stream.Null );

      }

      else

      {

        // This example does not concern itself with normal items

        reader.ReadItemData( Stream.Null );

      }

    }

  }

}
    Private Sub ReadZipArchive(ByVal archiveStream As Stream)

      ' Create the ZipReader object around the stream

      Using reader As New ZipReader(archiveStream)

        Dim header As ZipItemLocalHeader



        ' While the reader finds local headers

        header = reader.ReadItemLocalHeader()

        Do While header IsNot Nothing

'           The component will not automatically identity what is and is

'           * not a nested zip file. The application needs to have its own mechanism.

'           *

'           * This example will keep it simple and just look at the item's file name extension. 



          ' If the item's extension is .zip

          If StringComparer.OrdinalIgnoreCase.Compare(Path.GetExtension(header.FileName), ".zip") = 0 Then

            ' Create a read stream that wraps the item's data

            Using itemStream As Stream = reader.GetItemDataStream()

'               NOTE: While using a recursive call here makes for elegant and compact code that

'               * helps illustrate the concept of nested zip archives, a maliciously crafted zip file

'               * made up of a large number of nested zip files could make this code cause a stack overflow

'               * due to excessive recursion. 



              ' Call ourselves to read the nested archive

              Me.ReadZipArchive(itemStream)

            End Using



'             IMPORTANT: Before we can move on to the next item in the archive, ZipReader must have

'             * reached the end of the current item's data. Reading a nested zip file does not ensure

'             * this, so we need to make sure here by reading any remaining data into a dummy stream.

'             *

'             * Failure to do this will result in a ZipReaderException that reports that the object

'             * is not in the correct state to read the next item header. 



            ' Make sure we reach the end of the item's data

            reader.ReadItemData(Stream.Null)

          Else

            ' This example does not concern itself with normal items

            reader.ReadItemData(Stream.Null)

          End If

          header = reader.ReadItemLocalHeader()

        Loop

      End Using

    End Sub