// 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 OfficeOpenXml.Packaging.Ionic.Zip { internal partial class ZipFile { /// /// Adds an item, either a file or a directory, to a zip file archive. /// /// /// /// /// 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 ZipFile. /// /// /// /// The name of the item may be a relative path or a fully-qualified /// path. Remember, the items contained in ZipFile instance get written /// to the disk only when you call or a similar /// save method. /// /// /// /// The directory name used for the file within the archive is the same /// as the directory name (potentially a relative path) specified in the /// . /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// /// /// /// This method has two overloads. /// /// the name of the file or directory to add. /// /// The ZipEntry added. public ZipEntry AddItem(string fileOrDirectoryName) { return AddItem(fileOrDirectoryName, null); } /// /// 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. /// /// /// /// /// If adding a directory, the add is recursive on all files and /// subdirectories contained within it. /// /// /// The name of the item may be a relative path or a fully-qualified path. /// The item added by this call to the ZipFile is not read from the /// disk nor written to the zip file archive until the application calls /// Save() on the ZipFile. /// /// /// /// 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. /// /// /// /// Encryption will be used on the file data if the Password has /// been set on the ZipFile object, prior to calling this method. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// Thrown if the file or directory passed in does not exist. /// /// /// the name of the file or directory to add. /// /// /// /// 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 null (Nothing in VB) will use the /// path on the fileOrDirectoryName. Passing the empty string ("") will /// insert the item at the root path within the archive. /// /// /// /// /// /// /// /// 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. /// /// /// 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 < 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); /// } /// /// /// /// 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 /// /// /// The ZipEntry added. 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)); } /// /// Adds a File to a Zip file archive. /// /// /// /// /// 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 ZipFile, does DotNetZip read the file from the filesystem and /// then write the content to the zip file archive. /// /// /// /// This method will throw an exception if an entry with the same name already /// exists in the ZipFile. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// /// 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. /// /// /// 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); /// } /// /// /// /// 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 /// /// /// /// This method has two overloads. /// /// /// /// /// /// /// 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. /// /// The ZipEntry corresponding to the File added. public ZipEntry AddFile(string fileName) { return AddFile(fileName, null); } /// /// Adds a File to a Zip file archive, potentially overriding the path to be /// used within the zip archive. /// /// /// /// /// The file added by this call to the ZipFile is not written to the /// zip file archive until the application calls Save() on the ZipFile. /// /// /// /// This method will throw an exception if an entry with the same name already /// exists in the ZipFile. /// /// /// /// This version of the method allows the caller to explicitly specify the /// directory path to be used in the archive. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// /// 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. /// /// /// 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); /// } /// /// /// /// 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 /// /// /// /// /// /// /// /// /// The name of the file to add. The name of the file may be a relative path /// or a fully-qualified path. /// /// /// /// 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 null (Nothing 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. /// /// /// The ZipEntry corresponding to the file added. 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); } /// /// This method removes a collection of entries from the ZipFile. /// /// /// /// 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<ZipEntry> (ICollection(Of ZipEntry) in VB), and pass /// that ICollection to this method. /// /// /// /// public void RemoveEntries(System.Collections.Generic.ICollection entriesToRemove) { if (entriesToRemove == null) throw new ArgumentNullException("entriesToRemove"); foreach (ZipEntry e in entriesToRemove) { this.RemoveEntry(e); } } /// /// This method removes a collection of entries from the ZipFile, by name. /// /// /// /// A collection of strings that refer to names of entries to be removed /// from the ZipFile. For example, you can pass in an array or a /// List of Strings that provide the names of entries to be removed. /// /// /// /// public void RemoveEntries(System.Collections.Generic.ICollection entriesToRemove) { if (entriesToRemove == null) throw new ArgumentNullException("entriesToRemove"); foreach (String e in entriesToRemove) { this.RemoveEntry(e); } } /// /// This method adds a set of files to the ZipFile. /// /// /// /// /// Use this method to add a set of files to the zip archive, in one call. /// For example, a list of files received from /// System.IO.Directory.GetFiles() can be added to a zip archive in one /// call. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to each /// ZipEntry added. /// /// /// /// /// 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. /// /// /// /// This example shows how to create a zip file, and add a few files into it. /// /// 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); /// } /// /// /// /// 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 /// /// /// /// public void AddFiles(System.Collections.Generic.IEnumerable fileNames) { this.AddFiles(fileNames, null); } /// /// Adds or updates a set of files in the ZipFile. /// /// /// /// /// Any files that already exist in the archive are updated. Any files that /// don't yet exist in the archive are added. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to each /// ZipEntry added. /// /// /// /// /// 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. /// /// public void UpdateFiles(System.Collections.Generic.IEnumerable fileNames) { this.UpdateFiles(fileNames, null); } /// /// Adds a set of files to the ZipFile, using the /// specified directory path in the archive. /// /// /// /// /// 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. /// /// /// /// For ZipFile properties including , , , , , , and , their respective values at the /// time of this call will be applied to each ZipEntry added. /// /// /// /// /// 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. /// /// /// /// 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 null (Nothing in /// VB) will use the path on each of the fileNames, if any. Passing /// the empty string ("") will insert the item at the root path within the /// archive. /// /// /// public void AddFiles(System.Collections.Generic.IEnumerable fileNames, String directoryPathInArchive) { AddFiles(fileNames, false, directoryPathInArchive); } /// /// Adds a set of files to the ZipFile, using the specified directory /// path in the archive, and preserving the full directory structure in the /// filenames. /// /// /// /// /// /// Think of the as a "root" or /// base directory used in the archive for the files that get added. when /// 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 preserveDirHierarchy /// 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 /// directoryPathInArchive. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to each /// ZipEntry added. /// /// /// /// /// /// 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. /// /// /// /// 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 null (Nothing in /// VB) will use the path on each of the fileNames, if any. Passing /// the empty string ("") will insert the item at the root path within the /// archive. /// /// /// /// whether the entries in the zip archive will reflect the directory /// hierarchy that is present in the various filenames. For example, if /// includes two paths, /// \Animalia\Chordata\Mammalia\Info.txt and /// \Plantae\Magnoliophyta\Dicotyledon\Info.txt, then calling this method /// with = false will /// result in an exception because of a duplicate entry name, while /// calling this method with = /// true will result in the full direcory paths being included in /// the entries added to the ZipFile. /// /// public void AddFiles(System.Collections.Generic.IEnumerable 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(); } /// /// Adds or updates a set of files to the ZipFile, using the specified /// directory path in the archive. /// /// /// /// /// /// Any files that already exist in the archive are updated. Any files that /// don't yet exist in the archive are added. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to each /// ZipEntry added. /// /// /// /// /// 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. /// /// /// /// 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 null (Nothing in /// VB) will use the path on each of the fileNames, if any. Passing /// the empty string ("") will insert the item at the root path within the /// archive. /// /// /// public void UpdateFiles(System.Collections.Generic.IEnumerable fileNames, String directoryPathInArchive) { if (fileNames == null) throw new ArgumentNullException("fileNames"); OnAddStarted(); foreach (var f in fileNames) this.UpdateFile(f, directoryPathInArchive); OnAddCompleted(); } /// /// Adds or Updates a File in a Zip file archive. /// /// /// /// /// 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 UpdateFile method might more accurately be /// called "AddOrUpdateFile". /// /// /// /// Upon success, there is no way for the application to learn whether the file /// was added versus updated. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// 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. /// /// /// 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(); /// } /// /// /// /// 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 /// /// /// /// /// /// /// /// /// 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. /// /// /// /// The ZipEntry corresponding to the File that was added or updated. /// public ZipEntry UpdateFile(string fileName) { return UpdateFile(fileName, null); } /// /// Adds or Updates a File in a Zip file archive. /// /// /// /// /// 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. /// /// /// /// 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. /// /// /// /// Upon success, there is no way for the application to learn if the file was /// added versus updated. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// /// /// /// 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. /// /// /// /// 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 /// null (Nothing 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. /// /// /// /// The ZipEntry corresponding to the File that was added or updated. /// 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); } /// /// Add or update a directory in a zip archive. /// /// /// /// If the specified directory does not exist in the archive, then this method /// is equivalent to calling AddDirectory(). 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. /// /// /// /// /// /// /// /// The path to the directory to be added to the zip archive, or updated in /// the zip archive. /// /// /// /// The ZipEntry corresponding to the Directory that was added or updated. /// public ZipEntry UpdateDirectory(string directoryName) { return UpdateDirectory(directoryName, null); } /// /// Add or update a directory in the zip archive at the specified root /// directory in the archive. /// /// /// /// If the specified directory does not exist in the archive, then this method /// is equivalent to calling AddDirectory(). 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. /// /// /// /// /// /// /// /// The path to the directory to be added to the zip archive, or updated /// in the zip archive. /// /// /// /// 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 files within the zip are /// later extracted, this is the path used for the extracted file. Passing /// null (Nothing in VB) will use the path on the /// directoryName, if any. Passing the empty string ("") will insert /// the item at the root path within the archive. /// /// /// /// The ZipEntry corresponding to the Directory that was added or updated. /// public ZipEntry UpdateDirectory(string directoryName, String directoryPathInArchive) { return this.AddOrUpdateDirectoryImpl(directoryName, directoryPathInArchive, AddOrUpdateAction.AddOrUpdate); } /// /// Add or update a file or directory in the zip archive. /// /// /// /// /// 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 ZipFile. Calling this method /// is equivalent to calling RemoveEntry() if an entry by the same name /// already exists, followed calling by AddItem(). /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// /// /// /// the path to the file or directory to be added or updated. /// public void UpdateItem(string itemName) { UpdateItem(itemName, null); } /// /// Add or update a file or directory. /// /// /// /// /// 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 ZipFile. Calling this method /// is equivalent to calling RemoveEntry(), if an entry by that name /// exists, and then calling AddItem(). /// /// /// /// 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 /// DirectoryPathInArchive. Extracting the entry from the archive will /// result in a file stored in that directory path. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// /// /// /// The path for the File or Directory to be added or updated. /// /// /// Specifies a directory path to use to override any path in the /// itemName. 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 /// null (Nothing in VB) will use the path on the /// itemName, if any. Passing the empty string ("") will insert the /// item at the root path within the archive. /// 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)); } /// /// Adds a named entry into the zip archive, taking content for the entry /// from a string. /// /// /// /// 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. /// /// /// /// The content of the file, should it be extracted from the zip. /// /// /// /// The name, including any path, to use for the entry within the archive. /// /// /// The ZipEntry added. /// /// /// /// This example shows how to add an entry to the zipfile, using a string as /// content for that entry. /// /// /// 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"); /// } /// /// /// /// 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 " & DateTime.Now.ToString("G")) /// zip1.Save("Content.zip") /// End Using /// End Sub /// /// 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 } /// /// Adds a named entry into the zip archive, taking content for the entry /// from a string, and using the specified text encoding. /// /// /// /// /// /// 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, 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. /// /// /// /// Most Encoding classes support a constructor that accepts a boolean, /// indicating whether to emit a BOM or not. For example see . /// /// /// /// /// /// The name, including any path, to use within the archive for the entry. /// /// /// /// The content of the file, should it be extracted from the zip. /// /// /// /// 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 . /// /// /// The ZipEntry added. /// 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. } /// /// Create an entry in the ZipFile using the given Stream /// as input. The entry will have the given filename. /// /// /// /// /// /// The application should provide an open, readable stream; in this case it /// will be read during the call to or one of /// its overloads. /// /// /// /// 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. /// /// /// /// In cases where a large number of streams will be added to the /// ZipFile, the application may wish to avoid maintaining all of the /// streams open simultaneously. To handle this situation, the application /// should use the /// overload. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// /// This example adds a single entry to a ZipFile via a Stream. /// /// /// /// 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 /// } /// } /// /// /// /// 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 /// /// /// /// /// /// /// The name, including any path, which is shown in the zip file for the added /// entry. /// /// /// The input stream from which to grab content for the file /// /// The ZipEntry added. 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); } /// /// Add a ZipEntry for which content is written directly by the application. /// /// /// /// /// 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. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// About progress events: When using the WriteDelegate, DotNetZip does /// not issue any SaveProgress events with EventType = /// Saving_EntryBytesRead. (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 /// Saving_Started, /// /// Saving_BeforeWriteEntry, and /// Saving_AfterWriteEntry. /// /// /// /// 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. /// /// /// /// 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. /// /// /// /// As you can imagine, this can cause performance problems for large /// streams, and it can lead to correctness problems when you use a /// WriteDelegate. 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. /// /// /// /// 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 the Workitem /// tracking this issue. /// /// /// /// /// the name of the entry to add /// the delegate which will write the entry content /// the ZipEntry added /// /// /// /// 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. /// /// /// 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); /// } /// /// /// /// /// /// 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 /// AddFile() 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 WriteDelegate to do it, in this way. /// /// /// 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); /// } /// } /// /// /// /// /// /// 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 AddFile() method. The key scenario for /// which the WriteDelegate 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 WriteDelegate would be appropriate for those cases as /// well. /// /// /// 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 <> 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 /// /// public ZipEntry AddEntry(string entryName, WriteDelegate writer) { ZipEntry ze = ZipEntry.CreateForWriter(entryName, writer); if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName); return _InternalAddEntry(ze); } /// /// Add an entry, for which the application will provide a stream /// containing the entry data, on a just-in-time basis. /// /// /// /// /// 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. /// /// /// /// These delegates are called from within the scope of the call to /// ZipFile.Save(). /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// /// This example uses anonymous methods in C# to open and close the /// source stream for the content for a zip entry. /// /// /// 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); /// } /// /// /// /// /// /// /// 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. /// /// /// /// 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 /// /// /// /// /// the name of the entry to add /// /// 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. /// /// /// 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. /// /// the ZipEntry added /// 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; } /// /// Updates the given entry in the ZipFile, using the given /// string as content for the ZipEntry. /// /// /// /// /// /// Calling this method is equivalent to removing the ZipEntry for /// the given file name and directory path, if it exists, and then calling /// . 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 . /// /// /// /// /// /// The name, including any path, to use within the archive for the entry. /// /// /// /// The content of the file, should it be extracted from the zip. /// /// /// The ZipEntry added. /// 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 } /// /// Updates the given entry in the ZipFile, using the given string as /// content for the ZipEntry. /// /// /// /// Calling this method is equivalent to removing the ZipEntry for the /// given file name and directory path, if it exists, and then calling . See the /// documentation for that method for further explanation. /// /// /// /// The name, including any path, to use within the archive for the entry. /// /// /// /// The content of the file, should it be extracted from the zip. /// /// /// /// The text encoding to use when encoding the string. Be aware: This is /// distinct from the text encoding used to encode the filename. See . /// /// /// The ZipEntry added. /// public ZipEntry UpdateEntry(string entryName, string content, System.Text.Encoding encoding) { RemoveEntryForUpdate(entryName); return AddEntry(entryName, content, encoding); } /// /// Updates the given entry in the ZipFile, using the given delegate /// as the source for content for the ZipEntry. /// /// /// /// Calling this method is equivalent to removing the ZipEntry for the /// given file name and directory path, if it exists, and then calling . See the /// documentation for that method for further explanation. /// /// /// /// The name, including any path, to use within the archive for the entry. /// /// /// the delegate which will write the entry content. /// /// The ZipEntry added. /// public ZipEntry UpdateEntry(string entryName, WriteDelegate writer) { RemoveEntryForUpdate(entryName); return AddEntry(entryName, writer); } /// /// Updates the given entry in the ZipFile, using the given delegates /// to open and close the stream that provides the content for the ZipEntry. /// /// /// /// Calling this method is equivalent to removing the ZipEntry for the /// given file name and directory path, if it exists, and then calling . See the /// documentation for that method for further explanation. /// /// /// /// The name, including any path, to use within the archive for the entry. /// /// /// /// the delegate that will be invoked to open the stream /// /// /// the delegate that will be invoked to close the stream /// /// /// The ZipEntry added or updated. /// public ZipEntry UpdateEntry(string entryName, OpenDelegate opener, CloseDelegate closer) { RemoveEntryForUpdate(entryName); return AddEntry(entryName, opener, closer); } /// /// Updates the given entry in the ZipFile, using the given stream as /// input, and the given filename and given directory Path. /// /// /// /// /// Calling the method is equivalent to calling RemoveEntry() if an /// entry by the same name already exists, and then calling AddEntry() /// with the given fileName and stream. /// /// /// /// The stream must be open and readable during the call to /// ZipFile.Save. You can dispense the stream on a just-in-time basis /// using the property. Check the /// documentation of that property for more information. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to the /// ZipEntry added. /// /// /// /// /// /// /// /// /// The name, including any path, to use within the archive for the entry. /// /// /// The input stream from which to read file data. /// The ZipEntry added. 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); } /// /// 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. /// /// /// The data to use for the entry. /// /// /// The name, including any path, to use within the archive for the entry. /// /// /// The ZipEntry added. 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); } /// /// Updates the given entry in the ZipFile, using the given byte /// array as content for the entry. /// /// /// /// Calling this method is equivalent to removing the ZipEntry /// for the given filename and directory path, if it exists, and then /// calling . See the /// documentation for that method for further explanation. /// /// /// /// The name, including any path, to use within the archive for the entry. /// /// /// The content to use for the ZipEntry. /// /// The ZipEntry added. /// 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; // } /// /// Adds the contents of a filesystem directory to a Zip file archive. /// /// /// /// /// /// 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. /// /// /// /// 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. /// /// /// /// 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. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to each /// ZipEntry added. /// /// /// /// /// /// /// /// /// /// This method has 2 overloads. /// /// The name of the directory to add. /// The ZipEntry added. public ZipEntry AddDirectory(string directoryName) { return AddDirectory(directoryName, null); } /// /// Adds the contents of a filesystem directory to a Zip file archive, /// overriding the path to be used for entries in the archive. /// /// /// /// /// 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. /// /// /// /// 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. /// /// /// /// For ZipFile properties including , , , , , /// , and , their /// respective values at the time of this call will be applied to each /// ZipEntry added. /// /// /// /// /// /// /// 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. /// /// /// /// public void ZipUp(string targetZip, string directory) /// { /// using (var zip = new ZipFile()) /// { /// zip.AddDirectory(directory, System.IO.Path.GetFileName(directory)); /// zip.Save(targetZip); /// } /// } /// /// /// /// /// /// /// /// The name of the directory to add. /// /// /// 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 null /// (Nothing in VB) or the empty string ("") will insert the items at /// the root path within the archive. /// /// /// The ZipEntry added. public ZipEntry AddDirectory(string directoryName, string directoryPathInArchive) { return AddOrUpdateDirectoryImpl(directoryName, directoryPathInArchive, AddOrUpdateAction.AddOnly); } /// /// Creates a directory in the zip archive. /// /// /// /// /// /// Use this when you want to create a directory in the archive but there is /// no corresponding filesystem representation for that directory. /// /// /// /// 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. /// /// /// /// /// /// The name of the directory to create in the archive. /// /// The ZipEntry added. 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; } } }