Xceed Real-Time Zip for .NET Documentation
Welcome to Xceed Real-Time Zip for .NET, .NET Standard & Xamarin / Task-Based Help / Reading and writing nested Zip archives

In This Topic
    Reading and writing nested Zip archives
    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