Skip to content

Unpack a Zip, including embedded zips, and re pack into a new zip or memorystream

nils måsén edited this page Aug 21, 2019 · 1 revision

Code Reference / Zip Samples / Unpack a Zip, including embedded zips, and re-pack into a new zip or memorystream

This sample illustrates two major aspects:

  • how to extract files from embedded zips (a zip within a zip).
  • how to rebuild the contents into a new zip (optionally changing compression levels and changing encryption).

This example includes disk vs memorystream.

C#

using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;

// This example illustrates reading an input disk file (or any input stream),
// extracting the individual files, including from embedded zipfiles,
// and writing them to a new zipfile with an output memorystream or disk file.
public void DoRebuildFile(string zipFileIn, string password) {

    using(var outputStream = new MemoryStream())
    // To output to a disk file, replace the above with
    //using(var outputStream = File.Create(newZipFileName);
    using(var zipOutStream = new ZipOutputStream(outputStream))
    {
    
        // Stops the Close also Closing the underlying stream.
        zipOutStream.IsStreamOwner = false;	

        zipOutStream.SetLevel(3);

        // Optionally set password
        zipOutStream.Password = password;

        using(var inStream = File.OpenRead(zipFileIn))
        {
            RecursiveExtractRebuild(zipOutStream, inStream);
        }

    }

    // At this point the underlying output memory stream (outputStream) contains the zip.
    // If outputting to a web response, see the "Create a Zip as a browser download attachment in IIS"
    // See the "Create a Zip to a memory stream or byte array" example for other output options.
}

// Recursively extract embedded zip files
private void RecursiveExtractRebuild(ZipOutputStream outZipStream, Stream inStream)
{
    var buffer = new byte[4096];
    using(var zipFile = new ZipFile(inStream, leaveOpen: true))
    {
        foreach (ZipEntry zipEntry in zipFile)
        {
            if (!zipEntry.IsFile)
                continue;

            // To omit folder use: 
            //var entryFileName = Path.GetFileName(zipEntry.Name) 
            var entryFileName = zipEntry.Name; 

            // Specify any other filtering here.

            using(var zipStream = zipFile.GetInputStream(zipEntry))
            {
                // Extract Zips-within-zips
                if (entryFileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
                {
                    RecursiveExtractRebuild(outZipStream, zipStream);
                } 
                else 
                {
                    outZipStream.PutNextEntry(new ZipEntry(entryFileName)
                    {
                        DateTime = zipEntry.DateTime,
                        Size = zipEntry.Size,
                    });

                    StreamUtils.Copy(zipStream, outZipStream, buffer);
                    outZipStream.CloseEntry();
                }
            }
        }
    }
}

Visual Basic

Imports ICSharpCode.SharpZipLib.Core
Imports ICSharpCode.SharpZipLib.Zip

Private _zipOut As ZipOutputStream
Private _buffer As Byte() = New Byte(4095) {}

' This example illustrates reading an input disk file (or any input stream),
' extracting the individual files, including from embedded zipfiles,
' and writing them to a new zipfile with an output memorystream or disk file.
'
Public Sub DoRebuildFile(zipFileIn As String, password As String)

    Dim inStream As Stream = File.OpenRead(zipFileIn)

    Dim outputMemStream As New MemoryStream()
    _zipOut = New ZipOutputStream(outputMemStream)
    _zipOut.IsStreamOwner = False	' False stops the Close also Closing the underlying stream.

    ' To output to a disk file, replace the above with
    '
    '   FileStream fsOut = File.Create(newZipFileName);
    '   _zipOut = new ZipOutputStream(fsOut);
    '   _zipOut.IsStreamOwner = true;	' Makes the Close also Close the underlying stream.

    _zipOut.SetLevel(3)
    _zipOut.Password = password		' optional
    RecursiveExtractRebuild(inStream)
    inStream.Close()

    ' Must finish the ZipOutputStream to finalise output before using outputMemStream.
    _zipOut.Close()

    outputMemStream.Position = 0

    ' At this point the underlying output memory stream (outputMemStream) contains the zip.
    ' If outputting to a web response, see the "Create a Zip as a browser download attachment in IIS" example above.
    ' See the "Create a Zip to a memory stream or byte array" example for other output options.
End Sub

' Calls itself recursively if embedded zip
'
Private Sub RecursiveExtractRebuild(str As Stream)

    Dim zipFile As New ZipFile(str)
    zipFile.IsStreamOwner = False

    For Each zipEntry As ZipEntry In zipFile
        If Not zipEntry.IsFile Then
            Continue For
        End If
        Dim entryFileName As [String] = zipEntry.Name	' or Path.GetFileName(zipEntry.Name) to omit folder
        ' Specify any other filtering here.

        Dim zipStream As Stream = zipFile.GetInputStream(zipEntry)
        ' Zips-within-zips are extracted. If you don't want this and wish to keep embedded zips as-is, just delete these 3 lines. 
        If entryFileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) Then
            RecursiveExtractRebuild(zipStream)
        Else
            Dim newEntry As New ZipEntry(entryFileName)
            newEntry.DateTime = zipEntry.DateTime
            newEntry.Size = zipEntry.Size
            ' Setting the Size will allow the zip to be unpacked by XP's built-in extractor and other older code.

            _zipOut.PutNextEntry(newEntry)

            StreamUtils.Copy(zipStream, _zipOut, _buffer)
            _zipOut.CloseEntry()
        End If
    Next
End Sub