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

    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