431 lines
13 KiB
C#
431 lines
13 KiB
C#
// SimpleZip.cs
|
|
//
|
|
// Copyright 2005 John Reilly
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
//
|
|
// Linking this library statically or dynamically with other modules is
|
|
// making a combined work based on this library. Thus, the terms and
|
|
// conditions of the GNU General Public License cover the whole
|
|
// combination.
|
|
//
|
|
// As a special exception, the copyright holders of this library give you
|
|
// permission to link this library with independent modules to produce an
|
|
// executable, regardless of the license terms of these independent
|
|
// modules, and to copy and distribute the resulting executable under
|
|
// terms of your choice, provided that you also meet, for each linked
|
|
// independent module, the terms and conditions of the license of that
|
|
// module. An independent module is a module which is not derived from
|
|
// or based on this library. If you modify this library, you may extend
|
|
// this exception to your version of the library, but you are not
|
|
// obligated to do so. If you do not wish to do so, delete this
|
|
// exception statement from your version.
|
|
|
|
|
|
using System;
|
|
using System.IO;
|
|
using ICSharpCode.SharpZipLib.Core;
|
|
|
|
namespace ICSharpCode.SharpZipLib.Zip
|
|
{
|
|
/// <summary>
|
|
/// FastZipEvents supports all events applicable to <see cref="FastZip">FastZip</see> operations.
|
|
/// </summary>
|
|
public class FastZipEvents
|
|
{
|
|
/// <summary>
|
|
/// Delegate to invoke when processing directories.
|
|
/// </summary>
|
|
public ProcessDirectoryDelegate ProcessDirectory;
|
|
|
|
/// <summary>
|
|
/// Delegate to invoke when processing files.
|
|
/// </summary>
|
|
public ProcessFileDelegate ProcessFile;
|
|
|
|
/// <summary>
|
|
/// Delegate to invoke when processing directory failures.
|
|
/// </summary>
|
|
public DirectoryFailureDelegate DirectoryFailure;
|
|
|
|
/// <summary>
|
|
/// Delegate to invoke when processing file failures.
|
|
/// </summary>
|
|
public FileFailureDelegate FileFailure;
|
|
|
|
/// <summary>
|
|
/// Raise the directory failure event.
|
|
/// </summary>
|
|
/// <param name="directory">The directory.</param>
|
|
/// <param name="e">The exception for this event.</param>
|
|
public void OnDirectoryFailure(string directory, Exception e)
|
|
{
|
|
if ( DirectoryFailure != null ) {
|
|
ScanFailureEventArgs args = new ScanFailureEventArgs(directory, e);
|
|
DirectoryFailure(this, args);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the file failure event.
|
|
/// </summary>
|
|
/// <param name="file">The file for this event.</param>
|
|
/// <param name="e">The exception for this event.</param>
|
|
public void OnFileFailure(string file, Exception e)
|
|
{
|
|
if ( FileFailure != null ) {
|
|
ScanFailureEventArgs args = new ScanFailureEventArgs(file, e);
|
|
FileFailure(this, args);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the ProcessFileEvent.
|
|
/// </summary>
|
|
/// <param name="file">The file for this event.</param>
|
|
public void OnProcessFile(string file)
|
|
{
|
|
if ( ProcessFile != null ) {
|
|
ScanEventArgs args = new ScanEventArgs(file);
|
|
ProcessFile(this, args);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the ProcessDirectoryEvent.
|
|
/// </summary>
|
|
/// <param name="directory">The directory for this event.</param>
|
|
/// <param name="hasMatchingFiles">Flag indicating if directory has matching files as determined by the current filter.</param>
|
|
public void OnProcessDirectory(string directory, bool hasMatchingFiles)
|
|
{
|
|
if ( ProcessDirectory != null ) {
|
|
DirectoryEventArgs args = new DirectoryEventArgs(directory, hasMatchingFiles);
|
|
ProcessDirectory(this, args);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// FastZip provides facilities for creating and extracting zip files.
|
|
/// Only relative paths are supported.
|
|
/// </summary>
|
|
public class FastZip
|
|
{
|
|
/// <summary>
|
|
/// Initialize a default instance of FastZip.
|
|
/// </summary>
|
|
public FastZip()
|
|
{
|
|
this.events = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialise a new instance of <see cref="FastZip"/>
|
|
/// </summary>
|
|
/// <param name="events"></param>
|
|
public FastZip(FastZipEvents events)
|
|
{
|
|
this.events = events;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines the desired handling when overwriting files.
|
|
/// </summary>
|
|
public enum Overwrite {
|
|
/// <summary>
|
|
/// Prompt the user to confirm overwriting
|
|
/// </summary>
|
|
Prompt,
|
|
/// <summary>
|
|
/// Never overwrite files.
|
|
/// </summary>
|
|
Never,
|
|
/// <summary>
|
|
/// Always overwrite files.
|
|
/// </summary>
|
|
Always
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get/set a value indicating wether empty directories should be created.
|
|
/// </summary>
|
|
public bool CreateEmptyDirectories
|
|
{
|
|
get { return createEmptyDirectories; }
|
|
set { createEmptyDirectories = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delegate called when confirming overwriting of files.
|
|
/// </summary>
|
|
public delegate bool ConfirmOverwriteDelegate(string fileName);
|
|
|
|
/// <summary>
|
|
/// Create a zip file.
|
|
/// </summary>
|
|
/// <param name="zipFileName">The name of the zip file to create.</param>
|
|
/// <param name="sourceDirectory">The directory to source files from.</param>
|
|
/// <param name="recurse">True to recurse directories, false for no recursion.</param>
|
|
/// <param name="fileFilter">The file filter to apply.</param>
|
|
/// <param name="directoryFilter">The directory filter to apply.</param>
|
|
public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter)
|
|
{
|
|
NameTransform = new ZipNameTransform(true, sourceDirectory);
|
|
this.sourceDirectory = sourceDirectory;
|
|
|
|
outputStream = new ZipOutputStream(File.Create(zipFileName));
|
|
try {
|
|
FileSystemScanner scanner = new FileSystemScanner(fileFilter, directoryFilter);
|
|
scanner.ProcessFile += new ProcessFileDelegate(ProcessFile);
|
|
if ( this.CreateEmptyDirectories ) {
|
|
scanner.ProcessDirectory += new ProcessDirectoryDelegate(ProcessDirectory);
|
|
}
|
|
scanner.Scan(sourceDirectory, recurse);
|
|
}
|
|
finally {
|
|
outputStream.Close();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a zip file/archive.
|
|
/// </summary>
|
|
/// <param name="zipFileName">The name of the zip file to create.</param>
|
|
/// <param name="sourceDirectory">The directory to obtain files and directories from.</param>
|
|
/// <param name="recurse">True to recurse directories, false for no recursion.</param>
|
|
/// <param name="fileFilter">The file filter to apply.</param>
|
|
public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter)
|
|
{
|
|
CreateZip(zipFileName, sourceDirectory, recurse, fileFilter, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extract the contents of a zip file.
|
|
/// </summary>
|
|
/// <param name="zipFileName">The zip file to extract from.</param>
|
|
/// <param name="targetDirectory">The directory to save extracted information in.</param>
|
|
/// <param name="fileFilter">A filter to apply to files.</param>
|
|
public void ExtractZip(string zipFileName, string targetDirectory, string fileFilter)
|
|
{
|
|
ExtractZip(zipFileName, targetDirectory, Overwrite.Always, null, fileFilter, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exatract the contents of a zip file.
|
|
/// </summary>
|
|
/// <param name="zipFileName">The zip file to extract from.</param>
|
|
/// <param name="targetDirectory">The directory to save extracted information in.</param>
|
|
/// <param name="overwrite">The style of <see cref="Overwrite">overwriting</see> to apply.</param>
|
|
/// <param name="confirmDelegate">A delegate to invoke when confirming overwriting.</param>
|
|
/// <param name="fileFilter">A filter to apply to files.</param>
|
|
/// <param name="directoryFilter">A filter to apply to directories.</param>
|
|
public void ExtractZip(string zipFileName, string targetDirectory,
|
|
Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate,
|
|
string fileFilter, string directoryFilter)
|
|
{
|
|
if ((overwrite == Overwrite.Prompt) && (confirmDelegate == null)) {
|
|
throw new ArgumentNullException("confirmDelegate");
|
|
}
|
|
this.overwrite = overwrite;
|
|
this.confirmDelegate = confirmDelegate;
|
|
this.targetDirectory = targetDirectory;
|
|
this.fileFilter = new NameFilter(fileFilter);
|
|
this.directoryFilter = new NameFilter(directoryFilter);
|
|
|
|
inputStream = new ZipInputStream(File.OpenRead(zipFileName));
|
|
|
|
try {
|
|
|
|
if (password != null) {
|
|
inputStream.Password = password;
|
|
}
|
|
|
|
ZipEntry entry;
|
|
while ( (entry = inputStream.GetNextEntry()) != null ) {
|
|
if ( this.directoryFilter.IsMatch(Path.GetDirectoryName(entry.Name)) && this.fileFilter.IsMatch(entry.Name) ) {
|
|
ExtractEntry(entry);
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
inputStream.Close();
|
|
}
|
|
}
|
|
|
|
void ProcessDirectory(object sender, DirectoryEventArgs e)
|
|
{
|
|
if ( !e.HasMatchingFiles && createEmptyDirectories ) {
|
|
if ( events != null ) {
|
|
events.OnProcessDirectory(e.Name, e.HasMatchingFiles);
|
|
}
|
|
|
|
if (e.Name != sourceDirectory) {
|
|
string cleanedName = nameTransform.TransformDirectory(e.Name);
|
|
ZipEntry entry = new ZipEntry(cleanedName);
|
|
outputStream.PutNextEntry(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessFile(object sender, ScanEventArgs e)
|
|
{
|
|
if ( events != null ) {
|
|
events.OnProcessFile(e.Name);
|
|
}
|
|
string cleanedName = nameTransform.TransformFile(e.Name);
|
|
ZipEntry entry = new ZipEntry(cleanedName);
|
|
outputStream.PutNextEntry(entry);
|
|
AddFileContents(e.Name);
|
|
}
|
|
|
|
void AddFileContents(string name)
|
|
{
|
|
if ( buffer == null ) {
|
|
buffer = new byte[4096];
|
|
}
|
|
|
|
FileStream stream = File.OpenRead(name);
|
|
try {
|
|
int length;
|
|
do {
|
|
length = stream.Read(buffer, 0, buffer.Length);
|
|
outputStream.Write(buffer, 0, length);
|
|
} while ( length > 0 );
|
|
}
|
|
finally {
|
|
stream.Close();
|
|
}
|
|
}
|
|
|
|
void ExtractFileEntry(ZipEntry entry, string targetName)
|
|
{
|
|
bool proceed = true;
|
|
if ((overwrite == Overwrite.Prompt) && (confirmDelegate != null)) {
|
|
if (File.Exists(targetName) == true) {
|
|
proceed = confirmDelegate(targetName);
|
|
}
|
|
}
|
|
|
|
if ( proceed ) {
|
|
|
|
if ( events != null ) {
|
|
events.OnProcessFile(entry.Name);
|
|
}
|
|
|
|
FileStream streamWriter = File.Create(targetName);
|
|
|
|
try {
|
|
if ( buffer == null ) {
|
|
buffer = new byte[4096];
|
|
}
|
|
|
|
int size;
|
|
|
|
do {
|
|
size = inputStream.Read(buffer, 0, buffer.Length);
|
|
streamWriter.Write(buffer, 0, size);
|
|
} while (size > 0);
|
|
}
|
|
finally {
|
|
streamWriter.Close();
|
|
}
|
|
|
|
if (restoreDateTime) {
|
|
File.SetLastWriteTime(targetName, entry.DateTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool NameIsValid(string name)
|
|
{
|
|
return name != null && name.Length > 0 && name.IndexOfAny(Path.InvalidPathChars) < 0;
|
|
}
|
|
|
|
void ExtractEntry(ZipEntry entry)
|
|
{
|
|
bool doExtraction = NameIsValid(entry.Name);
|
|
|
|
string dirName = null;
|
|
string targetName = null;
|
|
|
|
if ( doExtraction ) {
|
|
string entryFileName;
|
|
if (Path.IsPathRooted(entry.Name)) {
|
|
string workName = Path.GetPathRoot(entry.Name);
|
|
workName = entry.Name.Substring(workName.Length);
|
|
entryFileName = Path.Combine(Path.GetDirectoryName(workName), Path.GetFileName(entry.Name));
|
|
} else {
|
|
entryFileName = entry.Name;
|
|
}
|
|
|
|
targetName = Path.Combine(targetDirectory, entryFileName);
|
|
dirName = Path.GetDirectoryName(Path.GetFullPath(targetName));
|
|
|
|
doExtraction = doExtraction && (entryFileName.Length > 0);
|
|
}
|
|
|
|
if ( doExtraction && !Directory.Exists(dirName) )
|
|
{
|
|
if ( !entry.IsDirectory || this.CreateEmptyDirectories ) {
|
|
try {
|
|
Directory.CreateDirectory(dirName);
|
|
}
|
|
catch {
|
|
doExtraction = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( doExtraction && entry.IsFile ) {
|
|
ExtractFileEntry(entry, targetName);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get or set the <see cref="ZipNameTransform"> active when creating Zip files.</see>
|
|
/// </summary>
|
|
public ZipNameTransform NameTransform
|
|
{
|
|
get { return nameTransform; }
|
|
set {
|
|
if ( value == null ) {
|
|
nameTransform = new ZipNameTransform();
|
|
}
|
|
else {
|
|
nameTransform = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region Instance Fields
|
|
byte[] buffer;
|
|
ZipOutputStream outputStream;
|
|
ZipInputStream inputStream;
|
|
string password = null;
|
|
string targetDirectory;
|
|
string sourceDirectory;
|
|
NameFilter fileFilter;
|
|
NameFilter directoryFilter;
|
|
Overwrite overwrite;
|
|
ConfirmOverwriteDelegate confirmDelegate;
|
|
bool restoreDateTime = false;
|
|
bool createEmptyDirectories = false;
|
|
FastZipEvents events;
|
|
ZipNameTransform nameTransform;
|
|
#endregion
|
|
}
|
|
}
|