-
Notifications
You must be signed in to change notification settings - Fork 978
Zip Samples
(Back to Code Reference main page)
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 contents
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
This sample illustrates many aspects:
- multiple entries
- entry name correction
- passwords and encryption options
- Zip64 format issues.
While this example is purely disk files, the next following example shows how to use memory stream for input and output.
using System;
using System.IO;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
// Compresses the files in the nominated folder, and creates a zip file on disk named as outPathname.
//
public void CreateSample(string outPathname, string password, string folderName) {
FileStream fsOut = File.Create(outPathname);
ZipOutputStream zipStream = new ZipOutputStream(fsOut);
zipStream.SetLevel(3); //0-9, 9 being the highest level of compression
zipStream.Password = password; // optional. Null is the same as not setting. Required if using AES.
// This setting will strip the leading part of the folder path in the entries, to
// make the entries relative to the starting folder.
// To include the full path for each entry up to the drive root, assign folderOffset = 0.
int folderOffset = folderName.Length + (folderName.EndsWith("\\") ? 0 : 1);
CompressFolder(folderName, zipStream, folderOffset);
zipStream.IsStreamOwner = true; // Makes the Close also Close the underlying stream
zipStream.Close();
}
// Recurses down the folder structure
//
private void CompressFolder(string path, ZipOutputStream zipStream, int folderOffset) {
string[] files = Directory.GetFiles(path);
foreach (string filename in files) {
FileInfo fi = new FileInfo(filename);
string entryName = filename.Substring(folderOffset); // Makes the name in zip based on the folder
entryName = ZipEntry.CleanName(entryName); // Removes drive from name and fixes slash direction
ZipEntry newEntry = new ZipEntry(entryName);
newEntry.DateTime = fi.LastWriteTime; // Note the zip format stores 2 second granularity
// Specifying the AESKeySize triggers AES encryption. Allowable values are 0 (off), 128 or 256.
// A password on the ZipOutputStream is required if using AES.
// newEntry.AESKeySize = 256;
// To permit the zip to be unpacked by built-in extractor in WinXP and Server2003, WinZip 8, Java, and other older code,
// you need to do one of the following: Specify UseZip64.Off, or set the Size.
// If the file may be bigger than 4GB, or you do not need WinXP built-in compatibility, you do not need either,
// but the zip will be in Zip64 format which not all utilities can understand.
// zipStream.UseZip64 = UseZip64.Off;
newEntry.Size = fi.Length;
zipStream.PutNextEntry(newEntry);
// Zip the file in buffered chunks
// the "using" will close the stream even if an exception occurs
byte[ ] buffer = new byte[4096];
using (FileStream streamReader = File.OpenRead(filename)) {
StreamUtils.Copy(streamReader, zipStream, buffer);
}
zipStream.CloseEntry();
}
string[ ] folders = Directory.GetDirectories(path);
foreach (string folder in folders) {
CompressFolder(folder, zipStream, folderOffset);
}
}
Imports System
Imports System.IO
Imports ICSharpCode.SharpZipLib.Core
Imports ICSharpCode.SharpZipLib.Zip
' Compresses the files in the nominated folder, and creates a zip file on disk named as outPathname.
'
Public Sub CreateSample(outPathname As String, password As String, folderName As String)
Dim fsOut As FileStream = File.Create(outPathname)
Dim zipStream As New ZipOutputStream(fsOut)
zipStream.SetLevel(3) '0-9, 9 being the highest level of compression
zipStream.Password = password ' optional. Null is the same as not setting.
' This setting will strip the leading part of the folder path in the entries, to
' make the entries relative to the starting folder.
' To include the full path for each entry up to the drive root, assign folderOffset = 0.
Dim folderOffset As Integer = folderName.Length + (If(folderName.EndsWith("\"), 0, 1))
CompressFolder(folderName, zipStream, folderOffset)
zipStream.IsStreamOwner = True
' Makes the Close also Close the underlying stream
zipStream.Close()
End Sub
' Recurses down the folder structure
'
Private Sub CompressFolder(path As String, zipStream As ZipOutputStream, folderOffset As Integer)
Dim files As String() = Directory.GetFiles(path)
For Each filename As String In files
Dim fi As New FileInfo(filename)
Dim entryName As String = filename.Substring(folderOffset) ' Makes the name in zip based on the folder
entryName = ZipEntry.CleanName(entryName) ' Removes drive from name and fixes slash direction
Dim newEntry As New ZipEntry(entryName)
newEntry.DateTime = fi.LastWriteTime ' Note the zip format stores 2 second granularity
' Specifying the AESKeySize triggers AES encryption. Allowable values are 0 (off), 128 or 256.
' newEntry.AESKeySize = 256;
' To permit the zip to be unpacked by built-in extractor in WinXP and Server2003, WinZip 8, Java, and other older code,
' you need to do one of the following: Specify UseZip64.Off, or set the Size.
' If the file may be bigger than 4GB, or you do not need WinXP built-in compatibility, you do not need either,
' but the zip will be in Zip64 format which not all utilities can understand.
' zipStream.UseZip64 = UseZip64.Off;
newEntry.Size = fi.Length
zipStream.PutNextEntry(newEntry)
' Zip the file in buffered chunks
' the "using" will close the stream even if an exception occurs
Dim buffer As Byte() = New Byte(4095) {}
Using streamReader As FileStream = File.OpenRead(filename)
StreamUtils.Copy(streamReader, zipStream, buffer)
End Using
zipStream.CloseEntry()
Next
Dim folders As String() = Directory.GetDirectories(path)
For Each folder As String In folders
CompressFolder(folder, zipStream, folderOffset)
Next
End Sub
This sample concentrates on the differences for memorystream output, the most important of which is setting IsStreamOwner = false so that the Close (which is needed to finish up the output) does not close the underlying memorystream. For multiple entries, passwords, etc, see the code sample above.
using ICSharpCode.SharpZipLib.Zip;
// Compresses the supplied memory stream, naming it as zipEntryName, into a zip,
// which is returned as a memory stream or a byte array.
//
public MemoryStream CreateToMemoryStream(MemoryStream memStreamIn, string zipEntryName) {
MemoryStream outputMemStream = new MemoryStream();
ZipOutputStream zipStream = new ZipOutputStream(outputMemStream);
zipStream.SetLevel(3); //0-9, 9 being the highest level of compression
ZipEntry newEntry = new ZipEntry(zipEntryName);
newEntry.DateTime = DateTime.Now;
zipStream.PutNextEntry(newEntry);
StreamUtils.Copy(memStreamIn, zipStream, new byte[4096]);
zipStream.CloseEntry();
zipStream.IsStreamOwner = false; // False stops the Close also Closing the underlying stream.
zipStream.Close(); // Must finish the ZipOutputStream before using outputMemStream.
outputMemStream.Position = 0;
return outputMemStream;
// Alternative outputs:
// ToArray is the cleaner and easiest to use correctly with the penalty of duplicating allocated memory.
byte[] byteArrayOut = outputMemStream.ToArray();
// GetBuffer returns a raw buffer raw and so you need to account for the true length yourself.
byte[] byteArrayOut = outputMemStream.GetBuffer();
long len = outputMemStream.Length;
}
Imports ICSharpCode.SharpZipLib.Zip
' Compresses the supplied memory stream, naming it as zipEntryName, into a zip,
' which is returned as a memory stream or a byte array.
'
Public Function CreateToMemoryStream(memStreamIn As MemoryStream, zipEntryName As String) As MemoryStream
Dim outputMemStream As New MemoryStream()
Dim zipStream As New ZipOutputStream(outputMemStream)
zipStream.SetLevel(3) '0-9, 9 being the highest level of compression
Dim newEntry As New ZipEntry(zipEntryName)
newEntry.DateTime = DateTime.Now
zipStream.PutNextEntry(newEntry)
StreamUtils.Copy(memStreamIn, zipStream, New Byte(4095) {})
zipStream.CloseEntry()
zipStream.IsStreamOwner = False ' False stops the Close also Closing the underlying stream.
zipStream.Close() ' Must finish the ZipOutputStream before using outputMemStream.
outputMemStream.Position = 0
Return outputMemStream
' Alternative outputs:
' ToArray is the cleaner and easiest to use correctly with the penalty of duplicating allocated memory.
Dim byteArrayOut As Byte() = outputMemStream.ToArray()
' GetBuffer returns a raw buffer raw and so you need to account for the true length yourself.
Dim byteArrayOut As Byte() = outputMemStream.GetBuffer()
Dim len As Long = outputMemStream.Length
End Function
This sample creates a zip as a browser download.
By writing directly to the Response OutputStream, zip download starts immediately, which fixes timeout problems when zipping large files.
For inputting from a memorystream instead of disk files, or passwording etc, see the samples above.
// the aspx page has just one line e.g. <%@ Page language="c#" Codebehind=...
// but if you must run this from within a page that has other output, start with a Response.Clear();
using ICSharpCode.SharpZipLib.Zip;
// This will accumulate each of the files named in the fileList into a zip file,
// and stream it to the browser.
// This approach writes directly to the Response OutputStream.
// The browser starts to receive data immediately which should avoid timeout problems.
// This also avoids an intermediate memorystream, saving memory on large files.
//
private void DownloadZipToBrowser(List <string> zipFileList) {
Response.ContentType = "application/zip";
// If the browser is receiving a mangled zipfile, IIS Compression may cause this problem. Some members have found that
//Response.ContentType = "application/octet-stream" has solved this. May be specific to Internet Explorer.
Response.AppendHeader("content-disposition", "attachment; filename=\"Download.zip\"");
Response.CacheControl = "Private";
Response.Cache.SetExpires(DateTime.Now.AddMinutes(3)); // or put a timestamp in the filename in the content-disposition
byte[] buffer = new byte[4096];
ZipOutputStream zipOutputStream = new ZipOutputStream(Response.OutputStream);
zipOutputStream.SetLevel(3); //0-9, 9 being the highest level of compression
foreach (string fileName in zipFileList) {
Stream fs = File.OpenRead(fileName); // or any suitable inputstream
ZipEntry entry = new ZipEntry(ZipEntry.CleanName(fileName));
entry.Size = fs.Length;
// Setting the Size provides WinXP built-in extractor compatibility,
// but if not available, you can set zipOutputStream.UseZip64 = UseZip64.Off instead.
zipOutputStream.PutNextEntry(entry);
int count = fs.Read(buffer, 0, buffer.Length);
while (count > 0) {
zipOutputStream.Write(buffer, 0, count);
count = fs.Read(buffer, 0, buffer.Length);
if (!Response.IsClientConnected) {
break;
}
Response.Flush();
}
fs.Close();
}
zipOutputStream.Close();
Response.Flush();
Response.End();
}
' the aspx page has just one line e.g. <%@ Page language="vb" Codebehind=...
' but if you must run this from within a page that has other output, start with a Response.Clear()
Imports ICSharpCode.SharpZipLib.Zip
' This will accumulate each of the files named in the fileList into a zip file,
' and stream it to the browser.
' This approach writes directly to the Response OutputStream.
' The browser starts to receive data immediately which should avoid timeout problems.
' This also avoids an intermediate memorystream, saving memory on large files.
'
Private Sub DownloadZipToBrowser(zipFileList As List(Of String))
Response.ContentType = "application/zip"
' If the browser is receiving a mangled zipfile, IIS Compression may cause this problem. Some members have found that
'Response.ContentType = "application/octet-stream" has solved this. May be specific to Internet Explorer.
Response.AppendHeader("content-disposition", "attachment; filename=""Download.zip""")
response.CacheControl = "Private"
response.Cache.SetExpires(DateTime.Now.AddMinutes(3))
' or put a timestamp in the filename in the content-disposition
Dim buffer As Byte() = New Byte(4095) {}
Dim zipOutputStream As New ZipOutputStream(Response.OutputStream)
zipOutputStream.SetLevel(3) '0-9, 9 being the highest level of compression
For Each fileName As String In zipFileList
Dim fs As Stream = File.OpenRead(fileName)
' or any suitable inputstream
Dim entry As New ZipEntry(ZipEntry.CleanName(fileName))
entry.Size = fs.Length
' Setting the Size provides WinXP built-in extractor compatibility,
' but if not available, you can set zipOutputStream.UseZip64 = UseZip64.Off instead.
zipOutputStream.PutNextEntry(entry)
Dim count As Integer = fs.Read(buffer, 0, buffer.Length)
While count > 0
zipOutputStream.Write(buffer, 0, count)
count = fs.Read(buffer, 0, buffer.Length)
If Not Response.IsClientConnected Then
Exit While
End If
Response.Flush()
End While
fs.Close()
Next
zipOutputStream.Close()
Response.Flush()
Response.End()
End Sub
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
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
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);
// 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
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(); }
}
}