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