AirScout/DotNetZip/Zip/ZipFile.AddUpdate.cs

2183 wiersze
93 KiB
C#

// ZipFile.AddUpdate.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009-2011 Dino Chiesa.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2011-November-01 13:56:58>
//
// ------------------------------------------------------------------
//
// This module defines the methods for Adding and Updating entries in
// the ZipFile.
//
// ------------------------------------------------------------------
//
using System;
using System.IO;
using System.Collections.Generic;
namespace Ionic.Zip
{
public partial class ZipFile
{
/// <summary>
/// Adds an item, either a file or a directory, to a zip file archive.
/// </summary>
///
/// <remarks>
/// <para>
/// This method is handy if you are adding things to zip archive and don't
/// want to bother distinguishing between directories or files. Any files are
/// added as single entries. A directory added through this method is added
/// recursively: all files and subdirectories contained within the directory
/// are added to the <c>ZipFile</c>.
/// </para>
///
/// <para>
/// The name of the item may be a relative path or a fully-qualified
/// path. Remember, the items contained in <c>ZipFile</c> instance get written
/// to the disk only when you call <see cref="ZipFile.Save()"/> or a similar
/// save method.
/// </para>
///
/// <para>
/// The directory name used for the file within the archive is the same
/// as the directory name (potentially a relative path) specified in the
/// <paramref name="fileOrDirectoryName"/>.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
///
/// </remarks>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddFile(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string)"/>
///
/// <overloads>This method has two overloads.</overloads>
/// <param name="fileOrDirectoryName">
/// the name of the file or directory to add.</param>
///
/// <returns>The <c>ZipEntry</c> added.</returns>
public ZipEntry AddItem(string fileOrDirectoryName)
{
return AddItem(fileOrDirectoryName, null);
}
/// <summary>
/// Adds an item, either a file or a directory, to a zip file archive,
/// explicitly specifying the directory path to be used in the archive.
/// </summary>
///
/// <remarks>
/// <para>
/// If adding a directory, the add is recursive on all files and
/// subdirectories contained within it.
/// </para>
/// <para>
/// The name of the item may be a relative path or a fully-qualified path.
/// The item added by this call to the <c>ZipFile</c> is not read from the
/// disk nor written to the zip file archive until the application calls
/// Save() on the <c>ZipFile</c>.
/// </para>
///
/// <para>
/// This version of the method allows the caller to explicitly specify the
/// directory path to be used in the archive, which would override the
/// "natural" path of the filesystem file.
/// </para>
///
/// <para>
/// Encryption will be used on the file data if the <c>Password</c> has
/// been set on the <c>ZipFile</c> object, prior to calling this method.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
///
/// </remarks>
///
/// <exception cref="System.IO.FileNotFoundException">
/// Thrown if the file or directory passed in does not exist.
/// </exception>
///
/// <param name="fileOrDirectoryName">the name of the file or directory to add.
/// </param>
///
/// <param name="directoryPathInArchive">
/// The name of the directory path to use within the zip archive. This path
/// need not refer to an extant directory in the current filesystem. If the
/// files within the zip are later extracted, this is the path used for the
/// extracted file. Passing <c>null</c> (<c>Nothing</c> in VB) will use the
/// path on the fileOrDirectoryName. Passing the empty string ("") will
/// insert the item at the root path within the archive.
/// </param>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddFile(string, string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string, string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string, string)"/>
///
/// <example>
/// This example shows how to zip up a set of files into a flat hierarchy,
/// regardless of where in the filesystem the files originated. The resulting
/// zip archive will contain a toplevel directory named "flat", which itself
/// will contain files Readme.txt, MyProposal.docx, and Image1.jpg. A
/// subdirectory under "flat" called SupportFiles will contain all the files
/// in the "c:\SupportFiles" directory on disk.
///
/// <code>
/// String[] itemnames= {
/// "c:\\fixedContent\\Readme.txt",
/// "MyProposal.docx",
/// "c:\\SupportFiles", // a directory
/// "images\\Image1.jpg"
/// };
///
/// try
/// {
/// using (ZipFile zip = new ZipFile())
/// {
/// for (int i = 1; i &lt; itemnames.Length; i++)
/// {
/// // will add Files or Dirs, recurses and flattens subdirectories
/// zip.AddItem(itemnames[i],"flat");
/// }
/// zip.Save(ZipToCreate);
/// }
/// }
/// catch (System.Exception ex1)
/// {
/// System.Console.Error.WriteLine("exception: {0}", ex1);
/// }
/// </code>
///
/// <code lang="VB">
/// Dim itemnames As String() = _
/// New String() { "c:\fixedContent\Readme.txt", _
/// "MyProposal.docx", _
/// "SupportFiles", _
/// "images\Image1.jpg" }
/// Try
/// Using zip As New ZipFile
/// Dim i As Integer
/// For i = 1 To itemnames.Length - 1
/// ' will add Files or Dirs, recursing and flattening subdirectories.
/// zip.AddItem(itemnames(i), "flat")
/// Next i
/// zip.Save(ZipToCreate)
/// End Using
/// Catch ex1 As Exception
/// Console.Error.WriteLine("exception: {0}", ex1.ToString())
/// End Try
/// </code>
/// </example>
/// <returns>The <c>ZipEntry</c> added.</returns>
public ZipEntry AddItem(String fileOrDirectoryName, String directoryPathInArchive)
{
if (File.Exists(fileOrDirectoryName))
return AddFile(fileOrDirectoryName, directoryPathInArchive);
if (Directory.Exists(fileOrDirectoryName))
return AddDirectory(fileOrDirectoryName, directoryPathInArchive);
throw new FileNotFoundException(String.Format("That file or directory ({0}) does not exist!",
fileOrDirectoryName));
}
/// <summary>
/// Adds a File to a Zip file archive.
/// </summary>
/// <remarks>
///
/// <para>
/// This call collects metadata for the named file in the filesystem,
/// including the file attributes and the timestamp, and inserts that metadata
/// into the resulting ZipEntry. Only when the application calls Save() on
/// the <c>ZipFile</c>, does DotNetZip read the file from the filesystem and
/// then write the content to the zip file archive.
/// </para>
///
/// <para>
/// This method will throw an exception if an entry with the same name already
/// exists in the <c>ZipFile</c>.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
///
/// </remarks>
///
/// <example>
/// <para>
/// In this example, three files are added to a Zip archive. The ReadMe.txt
/// file will be placed in the root of the archive. The .png file will be
/// placed in a folder within the zip called photos\personal. The pdf file
/// will be included into a folder within the zip called Desktop.
/// </para>
/// <code>
/// try
/// {
/// using (ZipFile zip = new ZipFile())
/// {
/// zip.AddFile("c:\\photos\\personal\\7440-N49th.png");
/// zip.AddFile("c:\\Desktop\\2008-Regional-Sales-Report.pdf");
/// zip.AddFile("ReadMe.txt");
///
/// zip.Save("Package.zip");
/// }
/// }
/// catch (System.Exception ex1)
/// {
/// System.Console.Error.WriteLine("exception: " + ex1);
/// }
/// </code>
///
/// <code lang="VB">
/// Try
/// Using zip As ZipFile = New ZipFile
/// zip.AddFile("c:\photos\personal\7440-N49th.png")
/// zip.AddFile("c:\Desktop\2008-Regional-Sales-Report.pdf")
/// zip.AddFile("ReadMe.txt")
/// zip.Save("Package.zip")
/// End Using
/// Catch ex1 As Exception
/// Console.Error.WriteLine("exception: {0}", ex1.ToString)
/// End Try
/// </code>
/// </example>
///
/// <overloads>This method has two overloads.</overloads>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddItem(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string)"/>
///
/// <param name="fileName">
/// The name of the file to add. It should refer to a file in the filesystem.
/// The name of the file may be a relative path or a fully-qualified path.
/// </param>
/// <returns>The <c>ZipEntry</c> corresponding to the File added.</returns>
public ZipEntry AddFile(string fileName)
{
return AddFile(fileName, null);
}
/// <summary>
/// Adds a File to a Zip file archive, potentially overriding the path to be
/// used within the zip archive.
/// </summary>
///
/// <remarks>
/// <para>
/// The file added by this call to the <c>ZipFile</c> is not written to the
/// zip file archive until the application calls Save() on the <c>ZipFile</c>.
/// </para>
///
/// <para>
/// This method will throw an exception if an entry with the same name already
/// exists in the <c>ZipFile</c>.
/// </para>
///
/// <para>
/// This version of the method allows the caller to explicitly specify the
/// directory path to be used in the archive.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
///
/// </remarks>
///
/// <example>
/// <para>
/// In this example, three files are added to a Zip archive. The ReadMe.txt
/// file will be placed in the root of the archive. The .png file will be
/// placed in a folder within the zip called images. The pdf file will be
/// included into a folder within the zip called files\docs, and will be
/// encrypted with the given password.
/// </para>
/// <code>
/// try
/// {
/// using (ZipFile zip = new ZipFile())
/// {
/// // the following entry will be inserted at the root in the archive.
/// zip.AddFile("c:\\datafiles\\ReadMe.txt", "");
/// // this image file will be inserted into the "images" directory in the archive.
/// zip.AddFile("c:\\photos\\personal\\7440-N49th.png", "images");
/// // the following will result in a password-protected file called
/// // files\\docs\\2008-Regional-Sales-Report.pdf in the archive.
/// zip.Password = "EncryptMe!";
/// zip.AddFile("c:\\Desktop\\2008-Regional-Sales-Report.pdf", "files\\docs");
/// zip.Save("Archive.zip");
/// }
/// }
/// catch (System.Exception ex1)
/// {
/// System.Console.Error.WriteLine("exception: {0}", ex1);
/// }
/// </code>
///
/// <code lang="VB">
/// Try
/// Using zip As ZipFile = New ZipFile
/// ' the following entry will be inserted at the root in the archive.
/// zip.AddFile("c:\datafiles\ReadMe.txt", "")
/// ' this image file will be inserted into the "images" directory in the archive.
/// zip.AddFile("c:\photos\personal\7440-N49th.png", "images")
/// ' the following will result in a password-protected file called
/// ' files\\docs\\2008-Regional-Sales-Report.pdf in the archive.
/// zip.Password = "EncryptMe!"
/// zip.AddFile("c:\Desktop\2008-Regional-Sales-Report.pdf", "files\documents")
/// zip.Save("Archive.zip")
/// End Using
/// Catch ex1 As Exception
/// Console.Error.WriteLine("exception: {0}", ex1)
/// End Try
/// </code>
/// </example>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddItem(string,string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string, string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string,string)"/>
///
/// <param name="fileName">
/// The name of the file to add. The name of the file may be a relative path
/// or a fully-qualified path.
/// </param>
///
/// <param name="directoryPathInArchive">
/// Specifies a directory path to use to override any path in the fileName.
/// This path may, or may not, correspond to a real directory in the current
/// filesystem. If the files within the zip are later extracted, this is the
/// path used for the extracted file. Passing <c>null</c> (<c>Nothing</c> in
/// VB) will use the path on the fileName, if any. Passing the empty string
/// ("") will insert the item at the root path within the archive.
/// </param>
///
/// <returns>The <c>ZipEntry</c> corresponding to the file added.</returns>
public ZipEntry AddFile(string fileName, String directoryPathInArchive)
{
string nameInArchive = ZipEntry.NameInArchive(fileName, directoryPathInArchive);
ZipEntry ze = ZipEntry.CreateFromFile(fileName, nameInArchive);
if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", fileName);
return _InternalAddEntry(ze);
}
/// <summary>
/// This method removes a collection of entries from the <c>ZipFile</c>.
/// </summary>
///
/// <param name="entriesToRemove">
/// A collection of ZipEntry instances from this zip file to be removed. For
/// example, you can pass in an array of ZipEntry instances; or you can call
/// SelectEntries(), and then add or remove entries from that
/// ICollection&lt;ZipEntry&gt; (ICollection(Of ZipEntry) in VB), and pass
/// that ICollection to this method.
/// </param>
///
/// <seealso cref="Ionic.Zip.ZipFile.SelectEntries(String)" />
/// <seealso cref="Ionic.Zip.ZipFile.RemoveSelectedEntries(String)" />
public void RemoveEntries(System.Collections.Generic.ICollection<ZipEntry> entriesToRemove)
{
if (entriesToRemove == null)
throw new ArgumentNullException("entriesToRemove");
foreach (ZipEntry e in entriesToRemove)
{
this.RemoveEntry(e);
}
}
/// <summary>
/// This method removes a collection of entries from the <c>ZipFile</c>, by name.
/// </summary>
///
/// <param name="entriesToRemove">
/// A collection of strings that refer to names of entries to be removed
/// from the <c>ZipFile</c>. For example, you can pass in an array or a
/// List of Strings that provide the names of entries to be removed.
/// </param>
///
/// <seealso cref="Ionic.Zip.ZipFile.SelectEntries(String)" />
/// <seealso cref="Ionic.Zip.ZipFile.RemoveSelectedEntries(String)" />
public void RemoveEntries(System.Collections.Generic.ICollection<String> entriesToRemove)
{
if (entriesToRemove == null)
throw new ArgumentNullException("entriesToRemove");
foreach (String e in entriesToRemove)
{
this.RemoveEntry(e);
}
}
/// <summary>
/// This method adds a set of files to the <c>ZipFile</c>.
/// </summary>
///
/// <remarks>
/// <para>
/// Use this method to add a set of files to the zip archive, in one call.
/// For example, a list of files received from
/// <c>System.IO.Directory.GetFiles()</c> can be added to a zip archive in one
/// call.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to each
/// ZipEntry added.
/// </para>
/// </remarks>
///
/// <param name="fileNames">
/// The collection of names of the files to add. Each string should refer to a
/// file in the filesystem. The name of the file may be a relative path or a
/// fully-qualified path.
/// </param>
///
/// <example>
/// This example shows how to create a zip file, and add a few files into it.
/// <code>
/// String ZipFileToCreate = "archive1.zip";
/// String DirectoryToZip = "c:\\reports";
/// using (ZipFile zip = new ZipFile())
/// {
/// // Store all files found in the top level directory, into the zip archive.
/// String[] filenames = System.IO.Directory.GetFiles(DirectoryToZip);
/// zip.AddFiles(filenames);
/// zip.Save(ZipFileToCreate);
/// }
/// </code>
///
/// <code lang="VB">
/// Dim ZipFileToCreate As String = "archive1.zip"
/// Dim DirectoryToZip As String = "c:\reports"
/// Using zip As ZipFile = New ZipFile
/// ' Store all files found in the top level directory, into the zip archive.
/// Dim filenames As String() = System.IO.Directory.GetFiles(DirectoryToZip)
/// zip.AddFiles(filenames)
/// zip.Save(ZipFileToCreate)
/// End Using
/// </code>
/// </example>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
public void AddFiles(System.Collections.Generic.IEnumerable<String> fileNames)
{
this.AddFiles(fileNames, null);
}
/// <summary>
/// Adds or updates a set of files in the <c>ZipFile</c>.
/// </summary>
///
/// <remarks>
/// <para>
/// Any files that already exist in the archive are updated. Any files that
/// don't yet exist in the archive are added.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to each
/// ZipEntry added.
/// </para>
/// </remarks>
///
/// <param name="fileNames">
/// The collection of names of the files to update. Each string should refer to a file in
/// the filesystem. The name of the file may be a relative path or a fully-qualified path.
/// </param>
///
public void UpdateFiles(System.Collections.Generic.IEnumerable<String> fileNames)
{
this.UpdateFiles(fileNames, null);
}
/// <summary>
/// Adds a set of files to the <c>ZipFile</c>, using the
/// specified directory path in the archive.
/// </summary>
///
/// <remarks>
/// <para>
/// Any directory structure that may be present in the
/// filenames contained in the list is "flattened" in the
/// archive. Each file in the list is added to the archive in
/// the specified top-level directory.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see
/// cref="Encryption"/>, <see cref="Password"/>, <see
/// cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see
/// cref="ExtractExistingFile"/>, <see
/// cref="ZipErrorAction"/>, and <see
/// cref="CompressionLevel"/>, their respective values at the
/// time of this call will be applied to each ZipEntry added.
/// </para>
/// </remarks>
///
/// <param name="fileNames">
/// The names of the files to add. Each string should refer to
/// a file in the filesystem. The name of the file may be a
/// relative path or a fully-qualified path.
/// </param>
///
/// <param name="directoryPathInArchive">
/// Specifies a directory path to use to override any path in the file name.
/// Th is path may, or may not, correspond to a real directory in the current
/// filesystem. If the files within the zip are later extracted, this is the
/// path used for the extracted file. Passing <c>null</c> (<c>Nothing</c> in
/// VB) will use the path on each of the <c>fileNames</c>, if any. Passing
/// the empty string ("") will insert the item at the root path within the
/// archive.
/// </param>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
public void AddFiles(System.Collections.Generic.IEnumerable<String> fileNames, String directoryPathInArchive)
{
AddFiles(fileNames, false, directoryPathInArchive);
}
/// <summary>
/// Adds a set of files to the <c>ZipFile</c>, using the specified directory
/// path in the archive, and preserving the full directory structure in the
/// filenames.
/// </summary>
///
/// <remarks>
///
/// <para>
/// Think of the <paramref name="directoryPathInArchive"/> as a "root" or
/// base directory used in the archive for the files that get added. when
/// <paramref name="preserveDirHierarchy"/> is true, the hierarchy of files
/// found in the filesystem will be placed, with the hierarchy intact,
/// starting at that root in the archive. When <c>preserveDirHierarchy</c>
/// is false, the path hierarchy of files is flattned, and the flattened
/// set of files gets placed in the root within the archive as specified in
/// <c>directoryPathInArchive</c>.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to each
/// ZipEntry added.
/// </para>
///
/// </remarks>
///
/// <param name="fileNames">
/// The names of the files to add. Each string should refer to a file in the
/// filesystem. The name of the file may be a relative path or a
/// fully-qualified path.
/// </param>
///
/// <param name="directoryPathInArchive">
/// Specifies a directory path to use as a prefix for each entry name.
/// This path may, or may not, correspond to a real directory in the current
/// filesystem. If the files within the zip are later extracted, this is the
/// path used for the extracted file. Passing <c>null</c> (<c>Nothing</c> in
/// VB) will use the path on each of the <c>fileNames</c>, if any. Passing
/// the empty string ("") will insert the item at the root path within the
/// archive.
/// </param>
///
/// <param name="preserveDirHierarchy">
/// whether the entries in the zip archive will reflect the directory
/// hierarchy that is present in the various filenames. For example, if
/// <paramref name="fileNames"/> includes two paths,
/// \Animalia\Chordata\Mammalia\Info.txt and
/// \Plantae\Magnoliophyta\Dicotyledon\Info.txt, then calling this method
/// with <paramref name="preserveDirHierarchy"/> = <c>false</c> will
/// result in an exception because of a duplicate entry name, while
/// calling this method with <paramref name="preserveDirHierarchy"/> =
/// <c>true</c> will result in the full direcory paths being included in
/// the entries added to the ZipFile.
/// </param>
/// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
public void AddFiles(System.Collections.Generic.IEnumerable<String> fileNames,
bool preserveDirHierarchy,
String directoryPathInArchive)
{
if (fileNames == null)
throw new ArgumentNullException("fileNames");
_addOperationCanceled = false;
OnAddStarted();
if (preserveDirHierarchy)
{
foreach (var f in fileNames)
{
if (_addOperationCanceled) break;
if (directoryPathInArchive != null)
{
//string s = SharedUtilities.NormalizePath(Path.Combine(directoryPathInArchive, Path.GetDirectoryName(f)));
string s = Path.GetFullPath(Path.Combine(directoryPathInArchive, Path.GetDirectoryName(f)));
this.AddFile(f, s);
}
else
this.AddFile(f, null);
}
}
else
{
foreach (var f in fileNames)
{
if (_addOperationCanceled) break;
this.AddFile(f, directoryPathInArchive);
}
}
if (!_addOperationCanceled)
OnAddCompleted();
}
/// <summary>
/// Adds or updates a set of files to the <c>ZipFile</c>, using the specified
/// directory path in the archive.
/// </summary>
///
/// <remarks>
///
/// <para>
/// Any files that already exist in the archive are updated. Any files that
/// don't yet exist in the archive are added.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to each
/// ZipEntry added.
/// </para>
/// </remarks>
///
/// <param name="fileNames">
/// The names of the files to add or update. Each string should refer to a
/// file in the filesystem. The name of the file may be a relative path or a
/// fully-qualified path.
/// </param>
///
/// <param name="directoryPathInArchive">
/// Specifies a directory path to use to override any path in the file name.
/// This path may, or may not, correspond to a real directory in the current
/// filesystem. If the files within the zip are later extracted, this is the
/// path used for the extracted file. Passing <c>null</c> (<c>Nothing</c> in
/// VB) will use the path on each of the <c>fileNames</c>, if any. Passing
/// the empty string ("") will insert the item at the root path within the
/// archive.
/// </param>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
public void UpdateFiles(System.Collections.Generic.IEnumerable<String> fileNames, String directoryPathInArchive)
{
if (fileNames == null)
throw new ArgumentNullException("fileNames");
OnAddStarted();
foreach (var f in fileNames)
this.UpdateFile(f, directoryPathInArchive);
OnAddCompleted();
}
/// <summary>
/// Adds or Updates a File in a Zip file archive.
/// </summary>
///
/// <remarks>
/// <para>
/// This method adds a file to a zip archive, or, if the file already exists
/// in the zip archive, this method Updates the content of that given filename
/// in the zip archive. The <c>UpdateFile</c> method might more accurately be
/// called "AddOrUpdateFile".
/// </para>
///
/// <para>
/// Upon success, there is no way for the application to learn whether the file
/// was added versus updated.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
/// </remarks>
///
/// <example>
///
/// This example shows how to Update an existing entry in a zipfile. The first
/// call to UpdateFile adds the file to the newly-created zip archive. The
/// second call to UpdateFile updates the content for that file in the zip
/// archive.
///
/// <code>
/// using (ZipFile zip1 = new ZipFile())
/// {
/// // UpdateFile might more accurately be called "AddOrUpdateFile"
/// zip1.UpdateFile("MyDocuments\\Readme.txt");
/// zip1.UpdateFile("CustomerList.csv");
/// zip1.Comment = "This zip archive has been created.";
/// zip1.Save("Content.zip");
/// }
///
/// using (ZipFile zip2 = ZipFile.Read("Content.zip"))
/// {
/// zip2.UpdateFile("Updates\\Readme.txt");
/// zip2.Comment = "This zip archive has been updated: The Readme.txt file has been changed.";
/// zip2.Save();
/// }
///
/// </code>
/// <code lang="VB">
/// Using zip1 As New ZipFile
/// ' UpdateFile might more accurately be called "AddOrUpdateFile"
/// zip1.UpdateFile("MyDocuments\Readme.txt")
/// zip1.UpdateFile("CustomerList.csv")
/// zip1.Comment = "This zip archive has been created."
/// zip1.Save("Content.zip")
/// End Using
///
/// Using zip2 As ZipFile = ZipFile.Read("Content.zip")
/// zip2.UpdateFile("Updates\Readme.txt")
/// zip2.Comment = "This zip archive has been updated: The Readme.txt file has been changed."
/// zip2.Save
/// End Using
/// </code>
/// </example>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddFile(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string)"/>
///
/// <param name="fileName">
/// The name of the file to add or update. It should refer to a file in the
/// filesystem. The name of the file may be a relative path or a
/// fully-qualified path.
/// </param>
///
/// <returns>
/// The <c>ZipEntry</c> corresponding to the File that was added or updated.
/// </returns>
public ZipEntry UpdateFile(string fileName)
{
return UpdateFile(fileName, null);
}
/// <summary>
/// Adds or Updates a File in a Zip file archive.
/// </summary>
///
/// <remarks>
/// <para>
/// This method adds a file to a zip archive, or, if the file already exists
/// in the zip archive, this method Updates the content of that given filename
/// in the zip archive.
/// </para>
///
/// <para>
/// This version of the method allows the caller to explicitly specify the
/// directory path to be used in the archive. The entry to be added or
/// updated is found by using the specified directory path, combined with the
/// basename of the specified filename.
/// </para>
///
/// <para>
/// Upon success, there is no way for the application to learn if the file was
/// added versus updated.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
/// </remarks>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddFile(string,string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string,string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string,string)"/>
///
/// <param name="fileName">
/// The name of the file to add or update. It should refer to a file in the
/// filesystem. The name of the file may be a relative path or a
/// fully-qualified path.
/// </param>
///
/// <param name="directoryPathInArchive">
/// Specifies a directory path to use to override any path in the
/// <c>fileName</c>. This path may, or may not, correspond to a real
/// directory in the current filesystem. If the files within the zip are
/// later extracted, this is the path used for the extracted file. Passing
/// <c>null</c> (<c>Nothing</c> in VB) will use the path on the
/// <c>fileName</c>, if any. Passing the empty string ("") will insert the
/// item at the root path within the archive.
/// </param>
///
/// <returns>
/// The <c>ZipEntry</c> corresponding to the File that was added or updated.
/// </returns>
public ZipEntry UpdateFile(string fileName, String directoryPathInArchive)
{
// ideally this would all be transactional!
var key = ZipEntry.NameInArchive(fileName, directoryPathInArchive);
if (this[key] != null)
this.RemoveEntry(key);
return this.AddFile(fileName, directoryPathInArchive);
}
/// <summary>
/// Add or update a directory in a zip archive.
/// </summary>
///
/// <remarks>
/// If the specified directory does not exist in the archive, then this method
/// is equivalent to calling <c>AddDirectory()</c>. If the specified
/// directory already exists in the archive, then this method updates any
/// existing entries, and adds any new entries. Any entries that are in the
/// zip archive but not in the specified directory, are left alone. In other
/// words, the contents of the zip file will be a union of the previous
/// contents and the new files.
/// </remarks>
///
/// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string)"/>
///
/// <param name="directoryName">
/// The path to the directory to be added to the zip archive, or updated in
/// the zip archive.
/// </param>
///
/// <returns>
/// The <c>ZipEntry</c> corresponding to the Directory that was added or updated.
/// </returns>
public ZipEntry UpdateDirectory(string directoryName)
{
return UpdateDirectory(directoryName, null);
}
/// <summary>
/// Add or update a directory in the zip archive at the specified root
/// directory in the archive.
/// </summary>
///
/// <remarks>
/// If the specified directory does not exist in the archive, then this method
/// is equivalent to calling <c>AddDirectory()</c>. If the specified
/// directory already exists in the archive, then this method updates any
/// existing entries, and adds any new entries. Any entries that are in the
/// zip archive but not in the specified directory, are left alone. In other
/// words, the contents of the zip file will be a union of the previous
/// contents and the new files.
/// </remarks>
///
/// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string,string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string,string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string,string)"/>
///
/// <param name="directoryName">
/// The path to the directory to be added to the zip archive, or updated
/// in the zip archive.
/// </param>
///
/// <param name="directoryPathInArchive">
/// Specifies a directory path to use to override any path in the
/// <c>directoryName</c>. This path may, or may not, correspond to a real
/// directory in the current filesystem. If the files within the zip are
/// later extracted, this is the path used for the extracted file. Passing
/// <c>null</c> (<c>Nothing</c> in VB) will use the path on the
/// <c>directoryName</c>, if any. Passing the empty string ("") will insert
/// the item at the root path within the archive.
/// </param>
///
/// <returns>
/// The <c>ZipEntry</c> corresponding to the Directory that was added or updated.
/// </returns>
public ZipEntry UpdateDirectory(string directoryName, String directoryPathInArchive)
{
return this.AddOrUpdateDirectoryImpl(directoryName, directoryPathInArchive, AddOrUpdateAction.AddOrUpdate);
}
/// <summary>
/// Add or update a file or directory in the zip archive.
/// </summary>
///
/// <remarks>
/// <para>
/// This is useful when the application is not sure or does not care if the
/// item to be added is a file or directory, and does not know or does not
/// care if the item already exists in the <c>ZipFile</c>. Calling this method
/// is equivalent to calling <c>RemoveEntry()</c> if an entry by the same name
/// already exists, followed calling by <c>AddItem()</c>.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
/// </remarks>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddItem(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string)"/>
///
/// <param name="itemName">
/// the path to the file or directory to be added or updated.
/// </param>
public void UpdateItem(string itemName)
{
UpdateItem(itemName, null);
}
/// <summary>
/// Add or update a file or directory.
/// </summary>
///
/// <remarks>
/// <para>
/// This method is useful when the application is not sure or does not care if
/// the item to be added is a file or directory, and does not know or does not
/// care if the item already exists in the <c>ZipFile</c>. Calling this method
/// is equivalent to calling <c>RemoveEntry()</c>, if an entry by that name
/// exists, and then calling <c>AddItem()</c>.
/// </para>
///
/// <para>
/// This version of the method allows the caller to explicitly specify the
/// directory path to be used for the item being added to the archive. The
/// entry or entries that are added or updated will use the specified
/// <c>DirectoryPathInArchive</c>. Extracting the entry from the archive will
/// result in a file stored in that directory path.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
/// </remarks>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddItem(string, string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string, string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string, string)"/>
///
/// <param name="itemName">
/// The path for the File or Directory to be added or updated.
/// </param>
/// <param name="directoryPathInArchive">
/// Specifies a directory path to use to override any path in the
/// <c>itemName</c>. This path may, or may not, correspond to a real
/// directory in the current filesystem. If the files within the zip are
/// later extracted, this is the path used for the extracted file. Passing
/// <c>null</c> (<c>Nothing</c> in VB) will use the path on the
/// <c>itemName</c>, if any. Passing the empty string ("") will insert the
/// item at the root path within the archive.
/// </param>
public void UpdateItem(string itemName, string directoryPathInArchive)
{
if (File.Exists(itemName))
UpdateFile(itemName, directoryPathInArchive);
else if (Directory.Exists(itemName))
UpdateDirectory(itemName, directoryPathInArchive);
else
throw new FileNotFoundException(String.Format("That file or directory ({0}) does not exist!", itemName));
}
/// <summary>
/// Adds a named entry into the zip archive, taking content for the entry
/// from a string.
/// </summary>
///
/// <remarks>
/// Calling this method creates an entry using the given fileName and
/// directory path within the archive. There is no need for a file by the
/// given name to exist in the filesystem; the name is used within the zip
/// archive only. The content for the entry is encoded using the default text
/// encoding for the machine, or on Silverlight, using UTF-8.
/// </remarks>
///
/// <param name="content">
/// The content of the file, should it be extracted from the zip.
/// </param>
///
/// <param name="entryName">
/// The name, including any path, to use for the entry within the archive.
/// </param>
///
/// <returns>The <c>ZipEntry</c> added.</returns>
///
/// <example>
///
/// This example shows how to add an entry to the zipfile, using a string as
/// content for that entry.
///
/// <code lang="C#">
/// string Content = "This string will be the content of the Readme.txt file in the zip archive.";
/// using (ZipFile zip1 = new ZipFile())
/// {
/// zip1.AddFile("MyDocuments\\Resume.doc", "files");
/// zip1.AddEntry("Readme.txt", Content);
/// zip1.Comment = "This zip file was created at " + System.DateTime.Now.ToString("G");
/// zip1.Save("Content.zip");
/// }
///
/// </code>
/// <code lang="VB">
/// Public Sub Run()
/// Dim Content As String = "This string will be the content of the Readme.txt file in the zip archive."
/// Using zip1 As ZipFile = New ZipFile
/// zip1.AddEntry("Readme.txt", Content)
/// zip1.AddFile("MyDocuments\Resume.doc", "files")
/// zip1.Comment = ("This zip file was created at " &amp; DateTime.Now.ToString("G"))
/// zip1.Save("Content.zip")
/// End Using
/// End Sub
/// </code>
/// </example>
public ZipEntry AddEntry(string entryName, string content)
{
#if SILVERLIGHT
return AddEntry(entryName, content, System.Text.Encoding.UTF8);
#else
return AddEntry(entryName, content, System.Text.Encoding.Default);
#endif
}
/// <summary>
/// Adds a named entry into the zip archive, taking content for the entry
/// from a string, and using the specified text encoding.
/// </summary>
///
/// <remarks>
///
/// <para>
/// Calling this method creates an entry using the given fileName and
/// directory path within the archive. There is no need for a file by the
/// given name to exist in the filesystem; the name is used within the zip
/// archive only.
/// </para>
///
/// <para>
/// The content for the entry, a string value, is encoded using the given
/// text encoding. A BOM (byte-order-mark) is emitted into the file, if the
/// Encoding parameter is set for that.
/// </para>
///
/// <para>
/// Most Encoding classes support a constructor that accepts a boolean,
/// indicating whether to emit a BOM or not. For example see <see
/// cref="System.Text.UTF8Encoding(bool)"/>.
/// </para>
///
/// </remarks>
///
/// <param name="entryName">
/// The name, including any path, to use within the archive for the entry.
/// </param>
///
/// <param name="content">
/// The content of the file, should it be extracted from the zip.
/// </param>
///
/// <param name="encoding">
/// The text encoding to use when encoding the string. Be aware: This is
/// distinct from the text encoding used to encode the fileName, as specified
/// in <see cref="ProvisionalAlternateEncoding" />.
/// </param>
///
/// <returns>The <c>ZipEntry</c> added.</returns>
///
public ZipEntry AddEntry(string entryName, string content, System.Text.Encoding encoding)
{
// cannot employ a using clause here. We need the stream to
// persist after exit from this method.
var ms = new MemoryStream();
// cannot use a using clause here; StreamWriter takes
// ownership of the stream and Disposes it before we are ready.
var sw = new StreamWriter(ms, encoding);
sw.Write(content);
sw.Flush();
// reset to allow reading later
ms.Seek(0, SeekOrigin.Begin);
return AddEntry(entryName, ms);
// must not dispose the MemoryStream - it will be used later.
}
/// <summary>
/// Create an entry in the <c>ZipFile</c> using the given <c>Stream</c>
/// as input. The entry will have the given filename.
/// </summary>
///
/// <remarks>
///
/// <para>
/// The application should provide an open, readable stream; in this case it
/// will be read during the call to <see cref="ZipFile.Save()"/> or one of
/// its overloads.
/// </para>
///
/// <para>
/// The passed stream will be read from its current position. If
/// necessary, callers should set the position in the stream before
/// calling AddEntry(). This might be appropriate when using this method
/// with a MemoryStream, for example.
/// </para>
///
/// <para>
/// In cases where a large number of streams will be added to the
/// <c>ZipFile</c>, the application may wish to avoid maintaining all of the
/// streams open simultaneously. To handle this situation, the application
/// should use the <see cref="AddEntry(string, OpenDelegate, CloseDelegate)"/>
/// overload.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
///
/// </remarks>
///
/// <example>
/// <para>
/// This example adds a single entry to a <c>ZipFile</c> via a <c>Stream</c>.
/// </para>
///
/// <code lang="C#">
/// String zipToCreate = "Content.zip";
/// String fileNameInArchive = "Content-From-Stream.bin";
/// using (System.IO.Stream streamToRead = MyStreamOpener())
/// {
/// using (ZipFile zip = new ZipFile())
/// {
/// ZipEntry entry= zip.AddEntry(fileNameInArchive, streamToRead);
/// zip.AddFile("Readme.txt");
/// zip.Save(zipToCreate); // the stream is read implicitly here
/// }
/// }
/// </code>
///
/// <code lang="VB">
/// Dim zipToCreate As String = "Content.zip"
/// Dim fileNameInArchive As String = "Content-From-Stream.bin"
/// Using streamToRead as System.IO.Stream = MyStreamOpener()
/// Using zip As ZipFile = New ZipFile()
/// Dim entry as ZipEntry = zip.AddEntry(fileNameInArchive, streamToRead)
/// zip.AddFile("Readme.txt")
/// zip.Save(zipToCreate) '' the stream is read implicitly, here
/// End Using
/// End Using
/// </code>
/// </example>
///
/// <seealso cref="Ionic.Zip.ZipFile.UpdateEntry(string, System.IO.Stream)"/>
///
/// <param name="entryName">
/// The name, including any path, which is shown in the zip file for the added
/// entry.
/// </param>
/// <param name="stream">
/// The input stream from which to grab content for the file
/// </param>
/// <returns>The <c>ZipEntry</c> added.</returns>
public ZipEntry AddEntry(string entryName, Stream stream)
{
ZipEntry ze = ZipEntry.CreateForStream(entryName, stream);
ze.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now);
if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
return _InternalAddEntry(ze);
}
/// <summary>
/// Add a ZipEntry for which content is written directly by the application.
/// </summary>
///
/// <remarks>
/// <para>
/// When the application needs to write the zip entry data, use this
/// method to add the ZipEntry. For example, in the case that the
/// application wishes to write the XML representation of a DataSet into
/// a ZipEntry, the application can use this method to do so.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
///
/// <para>
/// About progress events: When using the WriteDelegate, DotNetZip does
/// not issue any SaveProgress events with <c>EventType</c> = <see
/// cref="ZipProgressEventType.Saving_EntryBytesRead">
/// Saving_EntryBytesRead</see>. (This is because it is the
/// application's code that runs in WriteDelegate - there's no way for
/// DotNetZip to know when to issue a EntryBytesRead event.)
/// Applications that want to update a progress bar or similar status
/// indicator should do so from within the WriteDelegate
/// itself. DotNetZip will issue the other SaveProgress events,
/// including <see cref="ZipProgressEventType.Saving_Started">
/// Saving_Started</see>,
/// <see cref="ZipProgressEventType.Saving_BeforeWriteEntry">
/// Saving_BeforeWriteEntry</see>, and <see
/// cref="ZipProgressEventType.Saving_AfterWriteEntry">
/// Saving_AfterWriteEntry</see>.
/// </para>
///
/// <para>
/// Note: When you use PKZip encryption, it's normally necessary to
/// compute the CRC of the content to be encrypted, before compressing or
/// encrypting it. Therefore, when using PKZip encryption with a
/// WriteDelegate, the WriteDelegate CAN BE called twice: once to compute
/// the CRC, and the second time to potentially compress and
/// encrypt. Surprising, but true. This is because PKWARE specified that
/// the encryption initialization data depends on the CRC.
/// If this happens, for each call of the delegate, your
/// application must stream the same entry data in its entirety. If your
/// application writes different data during the second call, it will
/// result in a corrupt zip file.
/// </para>
///
/// <para>
/// The double-read behavior happens with all types of entries, not only
/// those that use WriteDelegate. It happens if you add an entry from a
/// filesystem file, or using a string, or a stream, or an opener/closer
/// pair. But in those cases, DotNetZip takes care of reading twice; in
/// the case of the WriteDelegate, the application code gets invoked
/// twice. Be aware.
/// </para>
///
/// <para>
/// As you can imagine, this can cause performance problems for large
/// streams, and it can lead to correctness problems when you use a
/// <c>WriteDelegate</c>. This is a pretty big pitfall. There are two
/// ways to avoid it. First, and most preferred: don't use PKZIP
/// encryption. If you use the WinZip AES encryption, this problem
/// doesn't occur, because the encryption protocol doesn't require the CRC
/// up front. Second: if you do choose to use PKZIP encryption, write out
/// to a non-seekable stream (like standard output, or the
/// Response.OutputStream in an ASP.NET application). In this case,
/// DotNetZip will use an alternative encryption protocol that does not
/// rely on the CRC of the content. This also implies setting bit 3 in
/// the zip entry, which still presents problems for some zip tools.
/// </para>
///
/// <para>
/// In the future I may modify DotNetZip to *always* use bit 3 when PKZIP
/// encryption is in use. This seems like a win overall, but there will
/// be some work involved. If you feel strongly about it, visit the
/// DotNetZip forums and vote up <see
/// href="http://dotnetzip.codeplex.com/workitem/13686">the Workitem
/// tracking this issue</see>.
/// </para>
///
/// </remarks>
///
/// <param name="entryName">the name of the entry to add</param>
/// <param name="writer">the delegate which will write the entry content</param>
/// <returns>the ZipEntry added</returns>
///
/// <example>
///
/// This example shows an application filling a DataSet, then saving the
/// contents of that DataSet as XML, into a ZipEntry in a ZipFile, using an
/// anonymous delegate in C#. The DataSet XML is never saved to a disk file.
///
/// <code lang="C#">
/// var c1= new System.Data.SqlClient.SqlConnection(connstring1);
/// var da = new System.Data.SqlClient.SqlDataAdapter()
/// {
/// SelectCommand= new System.Data.SqlClient.SqlCommand(strSelect, c1)
/// };
///
/// DataSet ds1 = new DataSet();
/// da.Fill(ds1, "Invoices");
///
/// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
/// {
/// zip.AddEntry(zipEntryName, (name,stream) => ds1.WriteXml(stream) );
/// zip.Save(zipFileName);
/// }
/// </code>
/// </example>
///
/// <example>
///
/// This example uses an anonymous method in C# as the WriteDelegate to provide
/// the data for the ZipEntry. The example is a bit contrived - the
/// <c>AddFile()</c> method is a simpler way to insert the contents of a file
/// into an entry in a zip file. On the other hand, if there is some sort of
/// processing or transformation of the file contents required before writing,
/// the application could use the <c>WriteDelegate</c> to do it, in this way.
///
/// <code lang="C#">
/// using (var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ))
/// {
/// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
/// {
/// zip.AddEntry(zipEntryName, (name,output) =>
/// {
/// byte[] buffer = new byte[BufferSize];
/// int n;
/// while ((n = input.Read(buffer, 0, buffer.Length)) != 0)
/// {
/// // could transform the data here...
/// output.Write(buffer, 0, n);
/// // could update a progress bar here
/// }
/// });
///
/// zip.Save(zipFileName);
/// }
/// }
/// </code>
/// </example>
///
/// <example>
///
/// This example uses a named delegate in VB to write data for the given
/// ZipEntry (VB9 does not have anonymous delegates). The example here is a bit
/// contrived - a simpler way to add the contents of a file to a ZipEntry is to
/// simply use the appropriate <c>AddFile()</c> method. The key scenario for
/// which the <c>WriteDelegate</c> makes sense is saving a DataSet, in XML
/// format, to the zip file. The DataSet can write XML to a stream, and the
/// WriteDelegate is the perfect place to write into the zip file. There may be
/// other data structures that can write to a stream, but cannot be read as a
/// stream. The <c>WriteDelegate</c> would be appropriate for those cases as
/// well.
///
/// <code lang="VB">
/// Private Sub WriteEntry (ByVal name As String, ByVal output As Stream)
/// Using input As FileStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
/// Dim n As Integer = -1
/// Dim buffer As Byte() = New Byte(BufferSize){}
/// Do While n &lt;&gt; 0
/// n = input.Read(buffer, 0, buffer.Length)
/// output.Write(buffer, 0, n)
/// Loop
/// End Using
/// End Sub
///
/// Public Sub Run()
/// Using zip = New ZipFile
/// zip.AddEntry(zipEntryName, New WriteDelegate(AddressOf WriteEntry))
/// zip.Save(zipFileName)
/// End Using
/// End Sub
/// </code>
/// </example>
public ZipEntry AddEntry(string entryName, WriteDelegate writer)
{
ZipEntry ze = ZipEntry.CreateForWriter(entryName, writer);
if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
return _InternalAddEntry(ze);
}
/// <summary>
/// Add an entry, for which the application will provide a stream
/// containing the entry data, on a just-in-time basis.
/// </summary>
///
/// <remarks>
/// <para>
/// In cases where the application wishes to open the stream that
/// holds the content for the ZipEntry, on a just-in-time basis, the
/// application can use this method. The application provides an
/// opener delegate that will be called by the DotNetZip library to
/// obtain a readable stream that can be read to get the bytes for
/// the given entry. Typically, this delegate opens a stream.
/// Optionally, the application can provide a closer delegate as
/// well, which will be called by DotNetZip when all bytes have been
/// read from the entry.
/// </para>
///
/// <para>
/// These delegates are called from within the scope of the call to
/// ZipFile.Save().
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
///
/// </remarks>
///
/// <example>
///
/// This example uses anonymous methods in C# to open and close the
/// source stream for the content for a zip entry.
///
/// <code lang="C#">
/// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
/// {
/// zip.AddEntry(zipEntryName,
/// (name) => File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ),
/// (name, stream) => stream.Close()
/// );
///
/// zip.Save(zipFileName);
/// }
/// </code>
///
/// </example>
///
/// <example>
///
/// This example uses delegates in VB.NET to open and close the
/// the source stream for the content for a zip entry. VB 9.0 lacks
/// support for "Sub" lambda expressions, and so the CloseDelegate must
/// be an actual, named Sub.
///
/// <code lang="VB">
///
/// Function MyStreamOpener(ByVal entryName As String) As Stream
/// '' This simply opens a file. You probably want to do somethinig
/// '' more involved here: open a stream to read from a database,
/// '' open a stream on an HTTP connection, and so on.
/// Return File.OpenRead(entryName)
/// End Function
///
/// Sub MyStreamCloser(entryName As String, stream As Stream)
/// stream.Close()
/// End Sub
///
/// Public Sub Run()
/// Dim dirToZip As String = "fodder"
/// Dim zipFileToCreate As String = "Archive.zip"
/// Dim opener As OpenDelegate = AddressOf MyStreamOpener
/// Dim closer As CloseDelegate = AddressOf MyStreamCloser
/// Dim numFilestoAdd As Int32 = 4
/// Using zip As ZipFile = New ZipFile
/// Dim i As Integer
/// For i = 0 To numFilesToAdd - 1
/// zip.AddEntry(String.Format("content-{0:000}.txt"), opener, closer)
/// Next i
/// zip.Save(zipFileToCreate)
/// End Using
/// End Sub
///
/// </code>
/// </example>
///
/// <param name="entryName">the name of the entry to add</param>
/// <param name="opener">
/// the delegate that will be invoked by ZipFile.Save() to get the
/// readable stream for the given entry. ZipFile.Save() will call
/// read on this stream to obtain the data for the entry. This data
/// will then be compressed and written to the newly created zip
/// file.
/// </param>
/// <param name="closer">
/// the delegate that will be invoked to close the stream. This may
/// be null (Nothing in VB), in which case no call is makde to close
/// the stream.
/// </param>
/// <returns>the ZipEntry added</returns>
///
public ZipEntry AddEntry(string entryName, OpenDelegate opener, CloseDelegate closer)
{
ZipEntry ze = ZipEntry.CreateForJitStreamProvider(entryName, opener, closer);
ze.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now);
if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
return _InternalAddEntry(ze);
}
private ZipEntry _InternalAddEntry(ZipEntry ze)
{
// stamp all the props onto the entry
ze._container = new ZipContainer(this);
ze.CompressionMethod = this.CompressionMethod;
ze.CompressionLevel = this.CompressionLevel;
ze.ExtractExistingFile = this.ExtractExistingFile;
ze.ZipErrorAction = this.ZipErrorAction;
ze.SetCompression = this.SetCompression;
ze.AlternateEncoding = this.AlternateEncoding;
ze.AlternateEncodingUsage = this.AlternateEncodingUsage;
ze.Password = this._Password;
ze.Encryption = this.Encryption;
ze.EmitTimesInWindowsFormatWhenSaving = this._emitNtfsTimes;
ze.EmitTimesInUnixFormatWhenSaving = this._emitUnixTimes;
//string key = DictionaryKeyForEntry(ze);
InternalAddEntry(ze.FileName,ze);
AfterAddEntry(ze);
return ze;
}
/// <summary>
/// Updates the given entry in the <c>ZipFile</c>, using the given
/// string as content for the <c>ZipEntry</c>.
/// </summary>
///
/// <remarks>
///
/// <para>
/// Calling this method is equivalent to removing the <c>ZipEntry</c> for
/// the given file name and directory path, if it exists, and then calling
/// <see cref="AddEntry(String,String)" />. See the documentation for
/// that method for further explanation. The string content is encoded
/// using the default encoding for the machine, or on Silverlight, using
/// UTF-8. This encoding is distinct from the encoding used for the
/// filename itself. See <see cref="AlternateEncoding"/>.
/// </para>
///
/// </remarks>
///
/// <param name="entryName">
/// The name, including any path, to use within the archive for the entry.
/// </param>
///
/// <param name="content">
/// The content of the file, should it be extracted from the zip.
/// </param>
///
/// <returns>The <c>ZipEntry</c> added.</returns>
///
public ZipEntry UpdateEntry(string entryName, string content)
{
#if SILVERLIGHT
return UpdateEntry(entryName, content, System.Text.Encoding.UTF8);
#else
return UpdateEntry(entryName, content, System.Text.Encoding.Default);
#endif
}
/// <summary>
/// Updates the given entry in the <c>ZipFile</c>, using the given string as
/// content for the <c>ZipEntry</c>.
/// </summary>
///
/// <remarks>
/// Calling this method is equivalent to removing the <c>ZipEntry</c> for the
/// given file name and directory path, if it exists, and then calling <see
/// cref="AddEntry(String,String, System.Text.Encoding)" />. See the
/// documentation for that method for further explanation.
/// </remarks>
///
/// <param name="entryName">
/// The name, including any path, to use within the archive for the entry.
/// </param>
///
/// <param name="content">
/// The content of the file, should it be extracted from the zip.
/// </param>
///
/// <param name="encoding">
/// The text encoding to use when encoding the string. Be aware: This is
/// distinct from the text encoding used to encode the filename. See <see
/// cref="AlternateEncoding" />.
/// </param>
///
/// <returns>The <c>ZipEntry</c> added.</returns>
///
public ZipEntry UpdateEntry(string entryName, string content, System.Text.Encoding encoding)
{
RemoveEntryForUpdate(entryName);
return AddEntry(entryName, content, encoding);
}
/// <summary>
/// Updates the given entry in the <c>ZipFile</c>, using the given delegate
/// as the source for content for the <c>ZipEntry</c>.
/// </summary>
///
/// <remarks>
/// Calling this method is equivalent to removing the <c>ZipEntry</c> for the
/// given file name and directory path, if it exists, and then calling <see
/// cref="AddEntry(String,WriteDelegate)" />. See the
/// documentation for that method for further explanation.
/// </remarks>
///
/// <param name="entryName">
/// The name, including any path, to use within the archive for the entry.
/// </param>
///
/// <param name="writer">the delegate which will write the entry content.</param>
///
/// <returns>The <c>ZipEntry</c> added.</returns>
///
public ZipEntry UpdateEntry(string entryName, WriteDelegate writer)
{
RemoveEntryForUpdate(entryName);
return AddEntry(entryName, writer);
}
/// <summary>
/// Updates the given entry in the <c>ZipFile</c>, using the given delegates
/// to open and close the stream that provides the content for the <c>ZipEntry</c>.
/// </summary>
///
/// <remarks>
/// Calling this method is equivalent to removing the <c>ZipEntry</c> for the
/// given file name and directory path, if it exists, and then calling <see
/// cref="AddEntry(String,OpenDelegate, CloseDelegate)" />. See the
/// documentation for that method for further explanation.
/// </remarks>
///
/// <param name="entryName">
/// The name, including any path, to use within the archive for the entry.
/// </param>
///
/// <param name="opener">
/// the delegate that will be invoked to open the stream
/// </param>
/// <param name="closer">
/// the delegate that will be invoked to close the stream
/// </param>
///
/// <returns>The <c>ZipEntry</c> added or updated.</returns>
///
public ZipEntry UpdateEntry(string entryName, OpenDelegate opener, CloseDelegate closer)
{
RemoveEntryForUpdate(entryName);
return AddEntry(entryName, opener, closer);
}
/// <summary>
/// Updates the given entry in the <c>ZipFile</c>, using the given stream as
/// input, and the given filename and given directory Path.
/// </summary>
///
/// <remarks>
/// <para>
/// Calling the method is equivalent to calling <c>RemoveEntry()</c> if an
/// entry by the same name already exists, and then calling <c>AddEntry()</c>
/// with the given <c>fileName</c> and stream.
/// </para>
///
/// <para>
/// The stream must be open and readable during the call to
/// <c>ZipFile.Save</c>. You can dispense the stream on a just-in-time basis
/// using the <see cref="ZipEntry.InputStream"/> property. Check the
/// documentation of that property for more information.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to the
/// <c>ZipEntry</c> added.
/// </para>
///
/// </remarks>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddEntry(string, System.IO.Stream)"/>
/// <seealso cref="Ionic.Zip.ZipEntry.InputStream"/>
///
/// <param name="entryName">
/// The name, including any path, to use within the archive for the entry.
/// </param>
///
/// <param name="stream">The input stream from which to read file data.</param>
/// <returns>The <c>ZipEntry</c> added.</returns>
public ZipEntry UpdateEntry(string entryName, Stream stream)
{
RemoveEntryForUpdate(entryName);
return AddEntry(entryName, stream);
}
private void RemoveEntryForUpdate(string entryName)
{
if (String.IsNullOrEmpty(entryName))
throw new ArgumentNullException("entryName");
string directoryPathInArchive = null;
if (entryName.IndexOf('\\') != -1)
{
directoryPathInArchive = Path.GetDirectoryName(entryName);
entryName = Path.GetFileName(entryName);
}
var key = ZipEntry.NameInArchive(entryName, directoryPathInArchive);
if (this[key] != null)
this.RemoveEntry(key);
}
/// <summary>
/// Add an entry into the zip archive using the given filename and
/// directory path within the archive, and the given content for the
/// file. No file is created in the filesystem.
/// </summary>
///
/// <param name="byteContent">The data to use for the entry.</param>
///
/// <param name="entryName">
/// The name, including any path, to use within the archive for the entry.
/// </param>
///
/// <returns>The <c>ZipEntry</c> added.</returns>
public ZipEntry AddEntry(string entryName, byte[] byteContent)
{
if (byteContent == null) throw new ArgumentException("bad argument", "byteContent");
var ms = new MemoryStream(byteContent);
return AddEntry(entryName, ms);
}
/// <summary>
/// Updates the given entry in the <c>ZipFile</c>, using the given byte
/// array as content for the entry.
/// </summary>
///
/// <remarks>
/// Calling this method is equivalent to removing the <c>ZipEntry</c>
/// for the given filename and directory path, if it exists, and then
/// calling <see cref="AddEntry(String,byte[])" />. See the
/// documentation for that method for further explanation.
/// </remarks>
///
/// <param name="entryName">
/// The name, including any path, to use within the archive for the entry.
/// </param>
///
/// <param name="byteContent">The content to use for the <c>ZipEntry</c>.</param>
///
/// <returns>The <c>ZipEntry</c> added.</returns>
///
public ZipEntry UpdateEntry(string entryName, byte[] byteContent)
{
RemoveEntryForUpdate(entryName);
return AddEntry(entryName, byteContent);
}
// private string DictionaryKeyForEntry(ZipEntry ze1)
// {
// var filename = SharedUtilities.NormalizePathForUseInZipFile(ze1.FileName);
// return filename;
// }
/// <summary>
/// Adds the contents of a filesystem directory to a Zip file archive.
/// </summary>
///
/// <remarks>
///
/// <para>
/// The name of the directory may be a relative path or a fully-qualified
/// path. Any files within the named directory are added to the archive. Any
/// subdirectories within the named directory are also added to the archive,
/// recursively.
/// </para>
///
/// <para>
/// Top-level entries in the named directory will appear as top-level entries
/// in the zip archive. Entries in subdirectories in the named directory will
/// result in entries in subdirectories in the zip archive.
/// </para>
///
/// <para>
/// If you want the entries to appear in a containing directory in the zip
/// archive itself, then you should call the AddDirectory() overload that
/// allows you to explicitly specify a directory path for use in the archive.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to each
/// ZipEntry added.
/// </para>
///
/// </remarks>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddItem(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.AddFile(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string, string)"/>
///
/// <overloads>This method has 2 overloads.</overloads>
///
/// <param name="directoryName">The name of the directory to add.</param>
/// <returns>The <c>ZipEntry</c> added.</returns>
public ZipEntry AddDirectory(string directoryName)
{
return AddDirectory(directoryName, null);
}
/// <summary>
/// Adds the contents of a filesystem directory to a Zip file archive,
/// overriding the path to be used for entries in the archive.
/// </summary>
///
/// <remarks>
/// <para>
/// The name of the directory may be a relative path or a fully-qualified
/// path. The add operation is recursive, so that any files or subdirectories
/// within the name directory are also added to the archive.
/// </para>
///
/// <para>
/// Top-level entries in the named directory will appear as top-level entries
/// in the zip archive. Entries in subdirectories in the named directory will
/// result in entries in subdirectories in the zip archive.
/// </para>
///
/// <para>
/// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
/// cref="Password"/>, <see cref="SetCompression"/>, <see
/// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
/// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
/// respective values at the time of this call will be applied to each
/// ZipEntry added.
/// </para>
///
/// </remarks>
///
/// <example>
/// <para>
/// In this code, calling the ZipUp() method with a value of "c:\reports" for
/// the directory parameter will result in a zip file structure in which all
/// entries are contained in a toplevel "reports" directory.
/// </para>
///
/// <code lang="C#">
/// public void ZipUp(string targetZip, string directory)
/// {
/// using (var zip = new ZipFile())
/// {
/// zip.AddDirectory(directory, System.IO.Path.GetFileName(directory));
/// zip.Save(targetZip);
/// }
/// }
/// </code>
/// </example>
///
/// <seealso cref="Ionic.Zip.ZipFile.AddItem(string, string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.AddFile(string, string)"/>
/// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string, string)"/>
///
/// <param name="directoryName">The name of the directory to add.</param>
///
/// <param name="directoryPathInArchive">
/// Specifies a directory path to use to override any path in the
/// DirectoryName. This path may, or may not, correspond to a real directory
/// in the current filesystem. If the zip is later extracted, this is the
/// path used for the extracted file or directory. Passing <c>null</c>
/// (<c>Nothing</c> in VB) or the empty string ("") will insert the items at
/// the root path within the archive.
/// </param>
///
/// <returns>The <c>ZipEntry</c> added.</returns>
public ZipEntry AddDirectory(string directoryName, string directoryPathInArchive)
{
return AddOrUpdateDirectoryImpl(directoryName, directoryPathInArchive, AddOrUpdateAction.AddOnly);
}
/// <summary>
/// Creates a directory in the zip archive.
/// </summary>
///
/// <remarks>
///
/// <para>
/// Use this when you want to create a directory in the archive but there is
/// no corresponding filesystem representation for that directory.
/// </para>
///
/// <para>
/// You will probably not need to do this in your code. One of the only times
/// you will want to do this is if you want an empty directory in the zip
/// archive. The reason: if you add a file to a zip archive that is stored
/// within a multi-level directory, all of the directory tree is implicitly
/// created in the zip archive.
/// </para>
///
/// </remarks>
///
/// <param name="directoryNameInArchive">
/// The name of the directory to create in the archive.
/// </param>
/// <returns>The <c>ZipEntry</c> added.</returns>
public ZipEntry AddDirectoryByName(string directoryNameInArchive)
{
// workitem 9073
ZipEntry dir = ZipEntry.CreateFromNothing(directoryNameInArchive);
dir._container = new ZipContainer(this);
dir.MarkAsDirectory();
dir.AlternateEncoding = this.AlternateEncoding; // workitem 8984
dir.AlternateEncodingUsage = this.AlternateEncodingUsage;
dir.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now);
dir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes;
dir.EmitTimesInUnixFormatWhenSaving = _emitUnixTimes;
dir._Source = ZipEntrySource.Stream;
//string key = DictionaryKeyForEntry(dir);
InternalAddEntry(dir.FileName,dir);
AfterAddEntry(dir);
return dir;
}
private ZipEntry AddOrUpdateDirectoryImpl(string directoryName,
string rootDirectoryPathInArchive,
AddOrUpdateAction action)
{
if (rootDirectoryPathInArchive == null)
{
rootDirectoryPathInArchive = "";
}
return AddOrUpdateDirectoryImpl(directoryName, rootDirectoryPathInArchive, action, true, 0);
}
internal void InternalAddEntry(String name, ZipEntry entry)
{
_entries.Add(name, entry);
_zipEntriesAsList = null;
_contentsChanged = true;
}
private ZipEntry AddOrUpdateDirectoryImpl(string directoryName,
string rootDirectoryPathInArchive,
AddOrUpdateAction action,
bool recurse,
int level)
{
if (Verbose)
StatusMessageTextWriter.WriteLine("{0} {1}...",
(action == AddOrUpdateAction.AddOnly) ? "adding" : "Adding or updating",
directoryName);
if (level == 0)
{
_addOperationCanceled = false;
OnAddStarted();
}
// workitem 13371
if (_addOperationCanceled)
return null;
string dirForEntries = rootDirectoryPathInArchive;
ZipEntry baseDir = null;
if (level > 0)
{
int f = directoryName.Length;
for (int i = level; i > 0; i--)
f = directoryName.LastIndexOfAny("/\\".ToCharArray(), f - 1, f - 1);
dirForEntries = directoryName.Substring(f + 1);
dirForEntries = Path.Combine(rootDirectoryPathInArchive, dirForEntries);
}
// if not top level, or if the root is non-empty, then explicitly add the directory
if (level > 0 || rootDirectoryPathInArchive != "")
{
baseDir = ZipEntry.CreateFromFile(directoryName, dirForEntries);
baseDir._container = new ZipContainer(this);
baseDir.AlternateEncoding = this.AlternateEncoding; // workitem 6410
baseDir.AlternateEncodingUsage = this.AlternateEncodingUsage;
baseDir.MarkAsDirectory();
baseDir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes;
baseDir.EmitTimesInUnixFormatWhenSaving = _emitUnixTimes;
// add the directory only if it does not exist.
// It's not an error if it already exists.
if (!_entries.ContainsKey(baseDir.FileName))
{
InternalAddEntry(baseDir.FileName,baseDir);
AfterAddEntry(baseDir);
}
dirForEntries = baseDir.FileName;
}
if (!_addOperationCanceled)
{
String[] filenames = Directory.GetFiles(directoryName);
if (recurse)
{
// add the files:
foreach (String filename in filenames)
{
if (_addOperationCanceled) break;
if (action == AddOrUpdateAction.AddOnly)
AddFile(filename, dirForEntries);
else
UpdateFile(filename, dirForEntries);
}
if (!_addOperationCanceled)
{
// add the subdirectories:
String[] dirnames = Directory.GetDirectories(directoryName);
foreach (String dir in dirnames)
{
// workitem 8617: Optionally traverse reparse points
#if SILVERLIGHT
#elif NETCF
FileAttributes fileAttrs = (FileAttributes) NetCfFile.GetAttributes(dir);
#else
FileAttributes fileAttrs = System.IO.File.GetAttributes(dir);
#endif
if (this.AddDirectoryWillTraverseReparsePoints
#if !SILVERLIGHT
|| ((fileAttrs & FileAttributes.ReparsePoint) == 0)
#endif
)
AddOrUpdateDirectoryImpl(dir, rootDirectoryPathInArchive, action, recurse, level + 1);
}
}
}
}
if (level == 0)
OnAddCompleted();
return baseDir;
}
}
}