Skip to content
nils måsén edited this page Aug 21, 2019 · 17 revisions

Code Reference / Zip Samples

How to use SharpZipLib to work with Zip files

These samples try to cover the range of situations you will encounter. You may need to combine parts of each sample for your application.

Table of Contents on this page

Create a Zip with full control over content
Create a Zip from/to a memory stream or byte array
Create a Zip as a browser download attachment in IIS Unpack a Zip with full control over the operation
Unpack a Zip - including embedded zips - and re-pack into a new zip or memorystream
Unpack a zip using ZipInputStream (eg for Unseekable input streams)
Download and Unpack a zip from an FTP server with recovery

Unpack a Zip with full control over the operation

This sample illustrates many aspects:

  • skipping directory entries
  • controlling the folder where the output is placed
  • passwords
  • exception handling, closing disk files, and efficient use of memory.

While this example is purely disk files, scroll down where other inputs and outputs such as memory stream are covered.

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

public void ExtractZipFile(string archiveFilenameIn, string password, string outFolder) {
	ZipFile zf = null;
	try {
		FileStream fs = File.OpenRead(archiveFilenameIn);
		zf = new ZipFile(fs);
		if (!String.IsNullOrEmpty(password)) {
			zf.Password = password;		// AES encrypted entries are handled automatically
		}
		foreach (ZipEntry zipEntry in zf) {
			if (!zipEntry.IsFile) {
				continue;			// Ignore directories
			}
			String entryFileName = zipEntry.Name;
			// to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
			// Optionally match entrynames against a selection list here to skip as desired.
			// The unpacked length is available in the zipEntry.Size property.

			byte[] buffer = new byte[4096];		// 4K is optimum
			Stream zipStream = zf.GetInputStream(zipEntry);

			// Manipulate the output filename here as desired.
			String fullZipToPath = Path.Combine(outFolder, entryFileName);
			string directoryName = Path.GetDirectoryName(fullZipToPath);
			if (directoryName.Length > 0)
				Directory.CreateDirectory(directoryName);

			// Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
			// of the file, but does not waste memory.
			// The "using" will close the stream even if an exception occurs.
			using (FileStream streamWriter = File.Create(fullZipToPath)) {
				StreamUtils.Copy(zipStream, streamWriter, buffer);
			}
		}
	} finally {
		if (zf != null) {
			zf.IsStreamOwner = true; // Makes close also shut the underlying stream
			zf.Close(); // Ensure we release resources
		}
	}
}
Imports ICSharpCode.SharpZipLib.Core
Imports ICSharpCode.SharpZipLib.Zip

Public Sub ExtractZipFile(archiveFilenameIn As String, password As String, outFolder As String)
	Dim zf As ZipFile = Nothing
	Try
		Dim fs As FileStream = File.OpenRead(archiveFilenameIn)
		zf = New ZipFile(fs)
		If Not [String].IsNullOrEmpty(password) Then	' AES encrypted entries are handled automatically
			zf.Password = password
		End If
		For Each zipEntry As ZipEntry In zf
			If Not zipEntry.IsFile Then		' Ignore directories
				Continue For
			End If
			Dim entryFileName As [String] = zipEntry.Name
			' to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
			' Optionally match entrynames against a selection list here to skip as desired.
			' The unpacked length is available in the zipEntry.Size property.

			Dim buffer As Byte() = New Byte(4095) {}	' 4K is optimum
			Dim zipStream As Stream = zf.GetInputStream(zipEntry)

			' Manipulate the output filename here as desired.
			Dim fullZipToPath As [String] = Path.Combine(outFolder, entryFileName)
			Dim directoryName As String = Path.GetDirectoryName(fullZipToPath)
			If directoryName.Length > 0 Then
				Directory.CreateDirectory(directoryName)
			End If

			' Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
			' of the file, but does not waste memory.
			' The "Using" will close the stream even if an exception occurs.
			Using streamWriter As FileStream = File.Create(fullZipToPath)
				StreamUtils.Copy(zipStream, streamWriter, buffer)
			End Using
		Next
	Finally
		If zf IsNot Nothing Then
			zf.IsStreamOwner = True		' Makes close also shut the underlying stream
			' Ensure we release resources
			zf.Close()
		End If
	End Try
End Sub

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.

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

private ZipOutputStream _zipOut;
private byte[] _buffer = new byte[4096];

// 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) {

	Stream inStream = File.OpenRead(zipFileIn);

	MemoryStream outputMemStream = 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.
}

// Calls itself recursively if embedded zip
//
private void RecursiveExtractRebuild(Stream str) {

	ZipFile zipFile = new ZipFile(str);
	zipFile.IsStreamOwner = false;
	
	foreach (ZipEntry zipEntry in zipFile) {
		if (!zipEntry.IsFile)
			continue;
		String entryFileName = zipEntry.Name; // or Path.GetFileName(zipEntry.Name) to omit folder
		// Specify any other filtering here.

		Stream zipStream = 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)) {
			RecursiveExtractRebuild(zipStream);
		} else {
			ZipEntry newEntry = 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();
		}
	}
}
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

Unpack a zip using ZipInputStream (eg for Unseekable input streams)

The ZipInputStream has one major advantage over using ZipFile to read a zip: it can read from an unseekable input stream - such as a WebClient download. However it currently cannot decode AES encrypted zips.

// Calling example:
	WebClient webClient = new WebClient();
	Stream data = webClient.OpenRead("http://www.example.com/test.zip");
	// This stream cannot be opened with the ZipFile class because CanSeek is false.
	UnzipFromStream(data, @"c:\temp");

public void UnzipFromStream(Stream zipStream, string outFolder) {

	ZipInputStream zipInputStream = new ZipInputStream(zipStream);
	ZipEntry zipEntry = zipInputStream.GetNextEntry();
	while (zipEntry != null) {
		String entryFileName = zipEntry.Name;
		// to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
		// Optionally match entrynames against a selection list here to skip as desired.
		// The unpacked length is available in the zipEntry.Size property.

		byte[] buffer = new byte[4096];		// 4K is optimum

		// Manipulate the output filename here as desired.
		String fullZipToPath = Path.Combine(outFolder, entryFileName);
		string directoryName = Path.GetDirectoryName(fullZipToPath);
		if (directoryName.Length > 0)
			Directory.CreateDirectory(directoryName);

		// Skip directory entry
		string fileName = Path.GetFileName(fullZipToPath);
		if (fileName.Length == 0)
                {
                    zipEntry = zipInputStream.GetNextEntry();
                    continue;
                }

		// Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
		// of the file, but does not waste memory.
		// The "using" will close the stream even if an exception occurs.
		using (FileStream streamWriter = File.Create(fullZipToPath)) {
			StreamUtils.Copy(zipInputStream, streamWriter, buffer);
		}
		zipEntry = zipInputStream.GetNextEntry();
	}
}
' Calling example:
	Dim webClient As new WebClient()
	Dim data As Stream = webClient.OpenRead("http://www.example.com/test.zip")
	' This stream cannot be opened with the ZipFile class because CanSeek is false.
	UnzipFromStream(data, "c:\temp")

Public Sub UnzipFromStream(zipStream As Stream, outFolder As String)

	Dim zipInputStream As New ZipInputStream(zipStream)
	Dim zipEntry As ZipEntry = zipInputStream.GetNextEntry()
	While zipEntry IsNot Nothing
		Dim entryFileName As [String] = zipEntry.Name
		' to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
		' Optionally match entrynames against a selection list here to skip as desired.
		' The unpacked length is available in the zipEntry.Size property.

		Dim buffer As Byte() = New Byte(4095) {}	' 4K is optimum

		' Manipulate the output filename here as desired.
		Dim fullZipToPath As [String] = Path.Combine(outFolder, entryFileName)
		Dim directoryName As String = Path.GetDirectoryName(fullZipToPath)
		If directoryName.Length > 0 Then
			Directory.CreateDirectory(directoryName)
		End If

		' Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
		' of the file, but does not waste memory.
		' The "using" will close the stream even if an exception occurs.
		Using streamWriter As FileStream = File.Create(fullZipToPath)
			StreamUtils.Copy(zipInputStream, streamWriter, buffer)
		End Using
		zipEntry = zipInputStream.GetNextEntry()
	End While
End Sub

Download and Unpack a zip from an FTP server with recovery

This example extends upon the previous, to provide for a recoverable FTP download and extract. Second draft.

public class FtpSample {

	public static void TestZipDownload() {

		// Create the FTP stream that takes care of restarting in event of failure.
		// This will hide any temporary problems from ZipInputStream
		//
		Stream ftpStream = new FtpDownloadStream("ftp://www.contoso.com/test.htm", "anonymous", "[email protected]");

		UnzipFromStream(ftpStream, @"c:\temp\out");
		ftpStream.Close();
	}

	// Insert the "UnzipFromStream" code from the sample, above.

}

// Implements a restartable Ftp Download and exposes the result as an uninterrupted stream.
public class FtpDownloadStream : Stream {
	private string _serverUri;
	private string _userName;
	private string _password;

	private FtpWebRequest _request;
	private FtpWebResponse _response;
	private Stream _responseStream;
	private int _totalDone;

	public Exception CaughtException;

	public FtpDownloadStream(string serverUri, string userName, string password) {
		_serverUri = serverUri;
		_userName = userName;
		_password = password;
	}

	private void StartFtpDownload() {
		// This can be replaced with the Http equivalents
		_request = (FtpWebRequest)WebRequest.Create(_serverUri);
		_request.Method = WebRequestMethods.Ftp.DownloadFile;
		_request.Credentials = new NetworkCredential(_userName, _password);
		_request.ContentOffset = _totalDone;		// for resume on failure

		_response = (FtpWebResponse)_request.GetResponse();
		_responseStream = _response.GetResponseStream();
		//if (_responseStream != null)
		//	_responseStream.ReadTimeout = 10000;	// Set timeout to 10 seconds for testing.
	}

	public override int Read(byte[] buffer, int offset, int count) {
		//
		int attempts = 0;
		while (attempts++ < 5) {	// Adjust the maximum attempts according to your needs
			if (_responseStream == null) {
				StartFtpDownload();
			}
			try {
				// This will throw a timeout exception if the connection is interrupted.
				// Will throw null exception if failed to open (start); this will also retry.
				int done = _responseStream.Read(buffer, offset, count);

				_totalDone += done;
				return done;
			}
			catch (Exception ex) {
				CaughtException = ex;
				// Close ftp resources if possible. Set instances to null to force restart. 
				Close();
			}
		}
		return 0;
	}

	public override void Close() {
		if (_responseStream != null) {
			try {
				_responseStream.Close();
				_response.Close();
			}
			catch {
				// No action required
			}
		}
		_responseStream = null;
		_response = null;
		_request = null;
	}

	// Implement the Stream methods
	public override void Flush() {
		throw new NotImplementedException(); 
	}
	public override long Seek(long offset, SeekOrigin origin) {
		throw new NotImplementedException();
	}
	public override void SetLength(long value) {
		throw new NotImplementedException();
	}
	public override void Write(byte[] buffer, int offset, int count) {
		throw new NotImplementedException();
	}
	public override bool CanRead {
		get { return true; }
	}
	public override bool CanSeek {
		get { return false; }
	}
	public override bool CanWrite {
		get { return false; }
	}
	public override long Length {
		get { throw new NotImplementedException(); }
	}
	public override long Position {
		get { throw new NotImplementedException(); }
		set { throw new NotImplementedException(); }
	}
}

Back to Code Reference main page

Clone this wiki locally