// ZipFile.cs // // Copyright (c) 2006-2010 Dino Chiesa // All rights reserved. // // This module is part of DotNetZip, a zipfile class library. // The class library reads and writes zip files, according to the format // described by PKware, at: // http://www.pkware.com/business_and_developers/developer/popups/appnote.txt // // // There are other Zip class libraries available. // // - it is possible to read and write zip files within .NET via the J# runtime. // But some people don't like to install the extra DLL, which is no longer // supported by MS. And also, the J# libraries don't support advanced zip // features, like ZIP64, spanned archives, or AES encryption. // // - There are third-party GPL and LGPL libraries available. Some people don't // like the license, and some of them don't support all the ZIP features, like AES. // // - Finally, there are commercial tools (From ComponentOne, XCeed, etc). But // some people don't want to incur the cost. // // This alternative implementation is **not** GPL licensed. It is free of cost, and // does not require J#. It does require .NET 2.0. It balances a good set of // features, with ease of use and speed of performance. // // This code is released under the Microsoft Public License . // See the License.txt for details. // // // NB: This implementation originally relied on the // System.IO.Compression.DeflateStream base class in the .NET Framework // v2.0 base class library, but now includes a managed-code port of Zlib. // // Thu, 08 Oct 2009 17:04 // using System; using System.IO; using System.Collections.Generic; using Interop = System.Runtime.InteropServices; namespace Ionic.Zip { /// /// The ZipFile type represents a zip archive file. /// /// /// /// /// This is the main type in the DotNetZip class library. This class reads and /// writes zip files, as defined in the specification /// for zip files described by PKWare. The compression for this /// implementation is provided by a managed-code version of Zlib, included with /// DotNetZip in the classes in the Ionic.Zlib namespace. /// /// /// /// This class provides a general purpose zip file capability. Use it to read, /// create, or update zip files. When you want to create zip files using a /// Stream type to write the zip file, you may want to consider the class. /// /// /// /// Both the ZipOutputStream class and the ZipFile class can /// be used to create zip files. Both of them support many of the common zip /// features, including Unicode, different compression methods and levels, /// and ZIP64. They provide very similar performance when creating zip /// files. /// /// /// /// The ZipFile class is generally easier to use than /// ZipOutputStream and should be considered a higher-level interface. For /// example, when creating a zip file via calls to the PutNextEntry() and /// Write() methods on the ZipOutputStream class, the caller is /// responsible for opening the file, reading the bytes from the file, writing /// those bytes into the ZipOutputStream, setting the attributes on the /// ZipEntry, and setting the created, last modified, and last accessed /// timestamps on the zip entry. All of these things are done automatically by a /// call to ZipFile.AddFile(). /// For this reason, the ZipOutputStream is generally recommended for use /// only when your application emits arbitrary data, not necessarily data from a /// filesystem file, directly into a zip file, and does so using a Stream /// metaphor. /// /// /// /// Aside from the differences in programming model, there are other /// differences in capability between the two classes. /// /// /// /// /// ZipFile can be used to read and extract zip files, in addition to /// creating zip files. ZipOutputStream cannot read zip files. If you want /// to use a stream to read zip files, check out the class. /// /// /// /// ZipOutputStream does not support the creation of segmented or spanned /// zip files. /// /// /// /// ZipOutputStream cannot produce a self-extracting archive. /// /// /// /// /// Be aware that the ZipFile class implements the interface. In order for ZipFile to /// produce a valid zip file, you use use it within a using clause (Using /// in VB), or call the Dispose() method explicitly. See the examples /// for how to employ a using clause. /// /// /// [Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00005")] [Interop.ComVisible(true)] #if !NETCF [Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)] #endif public partial class ZipFile : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable, IDisposable { #region public properties /// /// Indicates whether to perform a full scan of the zip file when reading it. /// /// /// /// /// /// You almost never want to use this property. /// /// /// /// When reading a zip file, if this flag is true (True in /// VB), the entire zip archive will be scanned and searched for entries. /// For large archives, this can take a very, long time. The much more /// efficient default behavior is to read the zip directory, which is /// stored at the end of the zip file. But, in some cases the directory is /// corrupted and you need to perform a full scan of the zip file to /// determine the contents of the zip file. This property lets you do /// that, when necessary. /// /// /// /// This flag is effective only when calling . Normally you would read a ZipFile with the /// static ZipFile.Read /// method. But you can't set the FullScan property on the /// ZipFile instance when you use a static factory method like /// ZipFile.Read. /// /// /// /// /// /// /// This example shows how to read a zip file using the full scan approach, /// and then save it, thereby producing a corrected zip file. /// /// /// using (var zip = new ZipFile()) /// { /// zip.FullScan = true; /// zip.Initialize(zipFileName); /// zip.Save(newName); /// } /// /// /// /// Using zip As New ZipFile /// zip.FullScan = True /// zip.Initialize(zipFileName) /// zip.Save(newName) /// End Using /// /// /// public bool FullScan { get; set; } /// /// Whether to sort the ZipEntries before saving the file. /// /// /// /// The default is false. If you have a large number of zip entries, the sort /// alone can consume significant time. /// /// /// /// /// using (var zip = new ZipFile()) /// { /// zip.AddFiles(filesToAdd); /// zip.SortEntriesBeforeSaving = true; /// zip.Save(name); /// } /// /// /// /// Using zip As New ZipFile /// zip.AddFiles(filesToAdd) /// zip.SortEntriesBeforeSaving = True /// zip.Save(name) /// End Using /// /// /// public bool SortEntriesBeforeSaving { get; set; } /// /// Indicates whether NTFS Reparse Points, like junctions, should be /// traversed during calls to AddDirectory(). /// /// /// /// By default, calls to AddDirectory() will traverse NTFS reparse /// points, like mounted volumes, and directory junctions. An example /// of a junction is the "My Music" directory in Windows Vista. In some /// cases you may not want DotNetZip to traverse those directories. In /// that case, set this property to false. /// /// /// /// /// using (var zip = new ZipFile()) /// { /// zip.AddDirectoryWillTraverseReparsePoints = false; /// zip.AddDirectory(dirToZip,"fodder"); /// zip.Save(zipFileToCreate); /// } /// /// public bool AddDirectoryWillTraverseReparsePoints { get; set; } /// /// Size of the IO buffer used while saving. /// /// /// /// /// /// First, let me say that you really don't need to bother with this. It is /// here to allow for optimizations that you probably won't make! It will work /// fine if you don't set or get this property at all. Ok? /// /// /// /// Now that we have that out of the way, the fine print: This /// property affects the size of the buffer that is used for I/O for each /// entry contained in the zip file. When a file is read in to be compressed, /// it uses a buffer given by the size here. When you update a zip file, the /// data for unmodified entries is copied from the first zip file to the /// other, through a buffer given by the size here. /// /// /// /// Changing the buffer size affects a few things: first, for larger buffer /// sizes, the memory used by the ZipFile, obviously, will be larger /// during I/O operations. This may make operations faster for very much /// larger files. Last, for any given entry, when you use a larger buffer /// there will be fewer progress events during I/O operations, because there's /// one progress event generated for each time the buffer is filled and then /// emptied. /// /// /// /// The default buffer size is 8k. Increasing the buffer size may speed /// things up as you compress larger files. But there are no hard-and-fast /// rules here, eh? You won't know til you test it. And there will be a /// limit where ever larger buffers actually slow things down. So as I said /// in the beginning, it's probably best if you don't set or get this property /// at all. /// /// /// /// /// /// This example shows how you might set a large buffer size for efficiency when /// dealing with zip entries that are larger than 1gb. /// /// using (ZipFile zip = new ZipFile()) /// { /// zip.SaveProgress += this.zip1_SaveProgress; /// zip.AddDirectory(directoryToZip, ""); /// zip.UseZip64WhenSaving = Zip64Option.Always; /// zip.BufferSize = 65536*8; // 65536 * 8 = 512k /// zip.Save(ZipFileToCreate); /// } /// /// public int BufferSize { get { return _BufferSize; } set { _BufferSize = value; } } /// /// Size of the work buffer to use for the ZLIB codec during compression. /// /// /// /// /// When doing ZLIB or Deflate compression, the library fills a buffer, /// then passes it to the compressor for compression. Then the library /// reads out the compressed bytes. This happens repeatedly until there /// is no more uncompressed data to compress. This property sets the /// size of the buffer that will be used for chunk-wise compression. In /// order for the setting to take effect, your application needs to set /// this property before calling one of the ZipFile.Save() /// overloads. /// /// /// Setting this affects the performance and memory efficiency of /// compression and decompression. For larger files, setting this to a /// larger size may improve compression performance, but the exact /// numbers vary depending on available memory, the size of the streams /// you are compressing, and a bunch of other variables. I don't have /// good firm recommendations on how to set it. You'll have to test it /// yourself. Or just leave it alone and accept the default. /// /// public int CodecBufferSize { get; set; } /// /// Indicates whether extracted files should keep their paths as /// stored in the zip archive. /// /// /// /// /// This property affects Extraction. It is not used when creating zip /// archives. /// /// /// /// With this property set to false, the default, extracting entries /// from a zip file will create files in the filesystem that have the full /// path associated to the entry within the zip file. With this property set /// to true, extracting entries from the zip file results in files /// with no path: the folders are "flattened." /// /// /// /// An example: suppose the zip file contains entries /directory1/file1.txt and /// /directory2/file2.txt. With FlattenFoldersOnExtract set to false, /// the files created will be \directory1\file1.txt and \directory2\file2.txt. /// With the property set to true, the files created are file1.txt and file2.txt. /// /// /// public bool FlattenFoldersOnExtract { get; set; } /// /// The compression strategy to use for all entries. /// /// /// /// Set the Strategy used by the ZLIB-compatible compressor, when /// compressing entries using the DEFLATE method. Different compression /// strategies work better on different sorts of data. The strategy /// parameter can affect the compression ratio and the speed of /// compression but not the correctness of the compresssion. For more /// information see Ionic.Zlib.CompressionStrategy. /// public Ionic.Zlib.CompressionStrategy Strategy { get { return _Strategy; } set { _Strategy = value; } } /// /// The name of the ZipFile, on disk. /// /// /// /// /// /// When the ZipFile instance was created by reading an archive using /// one of the ZipFile.Read methods, this property represents the name /// of the zip file that was read. When the ZipFile instance was /// created by using the no-argument constructor, this value is null /// (Nothing in VB). /// /// /// /// If you use the no-argument constructor, and you then explicitly set this /// property, when you call , this name will /// specify the name of the zip file created. Doing so is equivalent to /// calling . When instantiating a /// ZipFile by reading from a stream or byte array, the Name /// property remains null. When saving to a stream, the Name /// property is implicitly set to null. /// /// public string Name { get { return _name; } set { _name = value; } } /// /// Sets the compression level to be used for entries subsequently added to /// the zip archive. /// /// /// /// /// Varying the compression level used on entries can affect the /// size-vs-speed tradeoff when compression and decompressing data streams /// or files. /// /// /// /// As with some other properties on the ZipFile class, like , , and , setting this property on a ZipFile /// instance will cause the specified CompressionLevel to be used on all /// items that are subsequently added to the /// ZipFile instance. If you set this property after you have added /// items to the ZipFile, but before you have called Save(), /// those items will not use the specified compression level. /// /// /// /// If you do not set this property, the default compression level is used, /// which normally gives a good balance of compression efficiency and /// compression speed. In some tests, using BestCompression can /// double the time it takes to compress, while delivering just a small /// increase in compression efficiency. This behavior will vary with the /// type of data you compress. If you are in doubt, just leave this setting /// alone, and accept the default. /// /// public Ionic.Zlib.CompressionLevel CompressionLevel { get; set; } /// /// The compression method for the zipfile. /// /// /// /// By default, the compression method is CompressionMethod.Deflate. /// /// /// public Ionic.Zip.CompressionMethod CompressionMethod { get { return _compressionMethod; } set { _compressionMethod = value; } } /// /// A comment attached to the zip archive. /// /// /// /// /// /// This property is read/write. It allows the application to specify a /// comment for the ZipFile, or read the comment for the /// ZipFile. After setting this property, changes are only made /// permanent when you call a Save() method. /// /// /// /// According to PKWARE's /// zip specification, the comment is not encrypted, even if there is a /// password set on the zip file. /// /// /// /// The specification does not describe how to indicate the encoding used /// on a comment string. Many "compliant" zip tools and libraries use /// IBM437 as the code page for comments; DotNetZip, too, follows that /// practice. On the other hand, there are situations where you want a /// Comment to be encoded with something else, for example using code page /// 950 "Big-5 Chinese". To fill that need, DotNetZip will encode the /// comment following the same procedure it follows for encoding /// filenames: (a) if is /// Never, it uses the default encoding (IBM437). (b) if is Always, it always uses the /// alternate encoding (). (c) if is AsNecessary, it uses the /// alternate encoding only if the default encoding is not sufficient for /// encoding the comment - in other words if decoding the result does not /// produce the original string. This decision is taken at the time of /// the call to ZipFile.Save(). /// /// /// /// When creating a zip archive using this library, it is possible to change /// the value of between each /// entry you add, and between adding entries and the call to /// Save(). Don't do this. It will likely result in a zip file that is /// not readable by any tool or application. For best interoperability, leave /// alone, or specify it only /// once, before adding any entries to the ZipFile instance. /// /// /// public string Comment { get { return _Comment; } set { _Comment = value; _contentsChanged = true; } } /// /// Specifies whether the Creation, Access, and Modified times for entries /// added to the zip file will be emitted in “Windows format” /// when the zip archive is saved. /// /// /// /// /// An application creating a zip archive can use this flag to explicitly /// specify that the file times for the entries should or should not be stored /// in the zip archive in the format used by Windows. By default this flag is /// true, meaning the Windows-format times are stored in the zip /// archive. /// /// /// /// When adding an entry from a file or directory, the Creation (), Access (), and Modified () times for the given entry are /// automatically set from the filesystem values. When adding an entry from a /// stream or string, all three values are implicitly set to /// DateTime.Now. Applications can also explicitly set those times by /// calling . /// /// /// /// PKWARE's /// zip specification describes multiple ways to format these times in a /// zip file. One is the format Windows applications normally use: 100ns ticks /// since January 1, 1601 UTC. The other is a format Unix applications typically /// use: seconds since January 1, 1970 UTC. Each format can be stored in an /// "extra field" in the zip entry when saving the zip archive. The former /// uses an extra field with a Header Id of 0x000A, while the latter uses a /// header ID of 0x5455, although you probably don't need to know that. /// /// /// /// Not all tools and libraries can interpret these fields. Windows /// compressed folders is one that can read the Windows Format timestamps, /// while I believe the Infozip /// tools can read the Unix format timestamps. Some tools and libraries /// may be able to read only one or the other. DotNetZip can read or write /// times in either or both formats. /// /// /// /// The times stored are taken from , , and . /// /// /// /// The value set here applies to all entries subsequently added to the /// ZipFile. /// /// /// /// This property is not mutually exclusive of the property. It is possible and /// legal and valid to produce a zip file that contains timestamps encoded in /// the Unix format as well as in the Windows format, in addition to the LastModified time attached to each /// entry in the archive, a time that is always stored in "DOS format". And, /// notwithstanding the names PKWare uses for these time formats, any of them /// can be read and written by any computer, on any operating system. But, /// there are no guarantees that a program running on Mac or Linux will /// gracefully handle a zip file with "Windows" formatted times, or that an /// application that does not use DotNetZip but runs on Windows will be able to /// handle file times in Unix format. /// /// /// /// When in doubt, test. Sorry, I haven't got a complete list of tools and /// which sort of timestamps they can use and will tolerate. If you get any /// good information and would like to pass it on, please do so and I will /// include that information in this documentation. /// /// /// /// /// This example shows how to save a zip file that contains file timestamps /// in a format normally used by Unix. /// /// using (var zip = new ZipFile()) /// { /// // produce a zip file the Mac will like /// zip.EmitTimesInWindowsFormatWhenSaving = false; /// zip.EmitTimesInUnixFormatWhenSaving = true; /// zip.AddDirectory(directoryToZip, "files"); /// zip.Save(outputFile); /// } /// /// /// /// Using zip As New ZipFile /// '' produce a zip file the Mac will like /// zip.EmitTimesInWindowsFormatWhenSaving = False /// zip.EmitTimesInUnixFormatWhenSaving = True /// zip.AddDirectory(directoryToZip, "files") /// zip.Save(outputFile) /// End Using /// /// /// /// /// public bool EmitTimesInWindowsFormatWhenSaving { get { return _emitNtfsTimes; } set { _emitNtfsTimes = value; } } /// /// Specifies whether the Creation, Access, and Modified times /// for entries added to the zip file will be emitted in "Unix(tm) /// format" when the zip archive is saved. /// /// /// /// /// An application creating a zip archive can use this flag to explicitly /// specify that the file times for the entries should or should not be stored /// in the zip archive in the format used by Unix. By default this flag is /// false, meaning the Unix-format times are not stored in the zip /// archive. /// /// /// /// When adding an entry from a file or directory, the Creation (), Access (), and Modified () times for the given entry are /// automatically set from the filesystem values. When adding an entry from a /// stream or string, all three values are implicitly set to DateTime.Now. /// Applications can also explicitly set those times by calling . /// /// /// /// PKWARE's /// zip specification describes multiple ways to format these times in a /// zip file. One is the format Windows applications normally use: 100ns ticks /// since January 1, 1601 UTC. The other is a format Unix applications /// typically use: seconds since January 1, 1970 UTC. Each format can be /// stored in an "extra field" in the zip entry when saving the zip /// archive. The former uses an extra field with a Header Id of 0x000A, while /// the latter uses a header ID of 0x5455, although you probably don't need to /// know that. /// /// /// /// Not all tools and libraries can interpret these fields. Windows /// compressed folders is one that can read the Windows Format timestamps, /// while I believe the Infozip /// tools can read the Unix format timestamps. Some tools and libraries may be /// able to read only one or the other. DotNetZip can read or write times in /// either or both formats. /// /// /// /// The times stored are taken from , , and . /// /// /// /// This property is not mutually exclusive of the property. It is possible and /// legal and valid to produce a zip file that contains timestamps encoded in /// the Unix format as well as in the Windows format, in addition to the LastModified time attached to each /// entry in the zip archive, a time that is always stored in "DOS /// format". And, notwithstanding the names PKWare uses for these time /// formats, any of them can be read and written by any computer, on any /// operating system. But, there are no guarantees that a program running on /// Mac or Linux will gracefully handle a zip file with "Windows" formatted /// times, or that an application that does not use DotNetZip but runs on /// Windows will be able to handle file times in Unix format. /// /// /// /// When in doubt, test. Sorry, I haven't got a complete list of tools and /// which sort of timestamps they can use and will tolerate. If you get any /// good information and would like to pass it on, please do so and I will /// include that information in this documentation. /// /// /// /// /// public bool EmitTimesInUnixFormatWhenSaving { get { return _emitUnixTimes; } set { _emitUnixTimes = value; } } /// /// Indicates whether verbose output is sent to the during AddXxx() and /// ReadXxx() operations. /// /// /// /// This is a synthetic property. It returns true if the is non-null. /// internal bool Verbose { get { return (_StatusMessageTextWriter != null); } } /// /// Returns true if an entry by the given name exists in the ZipFile. /// /// /// the name of the entry to find /// true if an entry with the given name exists; otherwise false. /// public bool ContainsEntry(string name) { // workitem 12534 return _entries.ContainsKey(SharedUtilities.NormalizePathForUseInZipFile(name)); } /// /// Indicates whether to perform case-sensitive matching on the filename when /// retrieving entries in the zipfile via the string-based indexer. /// /// /// /// The default value is false, which means don't do case-sensitive /// matching. In other words, retrieving zip["ReadMe.Txt"] is the same as /// zip["readme.txt"]. It really makes sense to set this to true only /// if you are not running on Windows, which has case-insensitive /// filenames. But since this library is not built for non-Windows platforms, /// in most cases you should just leave this property alone. /// public bool CaseSensitiveRetrieval { get { return _CaseSensitiveRetrieval; } set { // workitem 9868 if (value != _CaseSensitiveRetrieval) { _CaseSensitiveRetrieval = value; _initEntriesDictionary(); } } } /// /// Indicates whether to encode entry filenames and entry comments using Unicode /// (UTF-8). /// /// /// /// /// The /// PKWare zip specification provides for encoding file names and file /// comments in either the IBM437 code page, or in UTF-8. This flag selects /// the encoding according to that specification. By default, this flag is /// false, and filenames and comments are encoded into the zip file in the /// IBM437 codepage. Setting this flag to true will specify that filenames /// and comments that cannot be encoded with IBM437 will be encoded with /// UTF-8. /// /// /// /// Zip files created with strict adherence to the PKWare specification with /// respect to UTF-8 encoding can contain entries with filenames containing /// any combination of Unicode characters, including the full range of /// characters from Chinese, Latin, Hebrew, Greek, Cyrillic, and many other /// alphabets. However, because at this time, the UTF-8 portion of the PKWare /// specification is not broadly supported by other zip libraries and /// utilities, such zip files may not be readable by your favorite zip tool or /// archiver. In other words, interoperability will decrease if you set this /// flag to true. /// /// /// /// In particular, Zip files created with strict adherence to the PKWare /// specification with respect to UTF-8 encoding will not work well with /// Explorer in Windows XP or Windows Vista, because Windows compressed /// folders, as far as I know, do not support UTF-8 in zip files. Vista can /// read the zip files, but shows the filenames incorrectly. Unpacking from /// Windows Vista Explorer will result in filenames that have rubbish /// characters in place of the high-order UTF-8 bytes. /// /// /// /// Also, zip files that use UTF-8 encoding will not work well with Java /// applications that use the java.util.zip classes, as of v5.0 of the Java /// runtime. The Java runtime does not correctly implement the PKWare /// specification in this regard. /// /// /// /// As a result, we have the unfortunate situation that "correct" behavior by /// the DotNetZip library with regard to Unicode encoding of filenames during /// zip creation will result in zip files that are readable by strictly /// compliant and current tools (for example the most recent release of the /// commercial WinZip tool); but these zip files will not be readable by /// various other tools or libraries, including Windows Explorer. /// /// /// /// The DotNetZip library can read and write zip files with UTF8-encoded /// entries, according to the PKware spec. If you use DotNetZip for both /// creating and reading the zip file, and you use UTF-8, there will be no /// loss of information in the filenames. For example, using a self-extractor /// created by this library will allow you to unpack files correctly with no /// loss of information in the filenames. /// /// /// /// If you do not set this flag, it will remain false. If this flag is false, /// your ZipFile will encode all filenames and comments using the /// IBM437 codepage. This can cause "loss of information" on some filenames, /// but the resulting zipfile will be more interoperable with other /// utilities. As an example of the loss of information, diacritics can be /// lost. The o-tilde character will be down-coded to plain o. The c with a /// cedilla (Unicode 0xE7) used in Portugese will be downcoded to a c. /// Likewise, the O-stroke character (Unicode 248), used in Danish and /// Norwegian, will be down-coded to plain o. Chinese characters cannot be /// represented in codepage IBM437; when using the default encoding, Chinese /// characters in filenames will be represented as ?. These are all examples /// of "information loss". /// /// /// /// The loss of information associated to the use of the IBM437 encoding is /// inconvenient, and can also lead to runtime errors. For example, using /// IBM437, any sequence of 4 Chinese characters will be encoded as ????. If /// your application creates a ZipFile, then adds two files, each with /// names of four Chinese characters each, this will result in a duplicate /// filename exception. In the case where you add a single file with a name /// containing four Chinese characters, calling Extract() on the entry that /// has question marks in the filename will result in an exception, because /// the question mark is not legal for use within filenames on Windows. These /// are just a few examples of the problems associated to loss of information. /// /// /// /// This flag is independent of the encoding of the content within the entries /// in the zip file. Think of the zip file as a container - it supports an /// encoding. Within the container are other "containers" - the file entries /// themselves. The encoding within those entries is independent of the /// encoding of the zip archive container for those entries. /// /// /// /// Rather than specify the encoding in a binary fashion using this flag, an /// application can specify an arbitrary encoding via the property. Setting the encoding /// explicitly when creating zip archives will result in non-compliant zip /// files that, curiously, are fairly interoperable. The challenge is, the /// PKWare specification does not provide for a way to specify that an entry /// in a zip archive uses a code page that is neither IBM437 nor UTF-8. /// Therefore if you set the encoding explicitly when creating a zip archive, /// you must take care upon reading the zip archive to use the same code page. /// If you get it wrong, the behavior is undefined and may result in incorrect /// filenames, exceptions, stomach upset, hair loss, and acne. /// /// /// [Obsolete("Beginning with v1.9.1.6 of DotNetZip, this property is obsolete. It will be removed in a future version of the library. Your applications should use AlternateEncoding and AlternateEncodingUsage instead.")] public bool UseUnicodeAsNecessary { get { return (_alternateEncoding == System.Text.Encoding.GetEncoding("UTF-8")) && (_alternateEncodingUsage == ZipOption.AsNecessary); } set { if (value) { _alternateEncoding = System.Text.Encoding.GetEncoding("UTF-8"); _alternateEncodingUsage = ZipOption.AsNecessary; } else { _alternateEncoding = Ionic.Zip.ZipFile.DefaultEncoding; _alternateEncodingUsage = ZipOption.Never; } } } /// /// Specify whether to use ZIP64 extensions when saving a zip archive. /// /// /// /// /// /// When creating a zip file, the default value for the property is . is /// safest, in the sense that you will not get an Exception if a pre-ZIP64 /// limit is exceeded. /// /// /// /// You may set the property at any time before calling Save(). /// /// /// /// When reading a zip file via the Zipfile.Read() method, DotNetZip /// will properly read ZIP64-endowed zip archives, regardless of the value of /// this property. DotNetZip will always read ZIP64 archives. This property /// governs only whether DotNetZip will write them. Therefore, when updating /// archives, be careful about setting this property after reading an archive /// that may use ZIP64 extensions. /// /// /// /// An interesting question is, if you have set this property to /// AsNecessary, and then successfully saved, does the resulting /// archive use ZIP64 extensions or not? To learn this, check the property, after calling Save(). /// /// /// /// Have you thought about /// donating? /// /// /// /// public Zip64Option UseZip64WhenSaving { get { return _zip64; } set { _zip64 = value; } } /// /// Indicates whether the archive requires ZIP64 extensions. /// /// /// /// /// /// This property is null (or Nothing in VB) if the archive has /// not been saved, and there are fewer than 65334 ZipEntry items /// contained in the archive. /// /// /// /// The Value is true if any of the following four conditions holds: /// the uncompressed size of any entry is larger than 0xFFFFFFFF; the /// compressed size of any entry is larger than 0xFFFFFFFF; the relative /// offset of any entry within the zip archive is larger than 0xFFFFFFFF; or /// there are more than 65534 entries in the archive. (0xFFFFFFFF = /// 4,294,967,295). The result may not be known until a Save() is attempted /// on the zip archive. The Value of this /// property may be set only AFTER one of the Save() methods has been called. /// /// /// /// If none of the four conditions holds, and the archive has been saved, then /// the Value is false. /// /// /// /// A Value of false does not indicate that the zip archive, as saved, /// does not use ZIP64. It merely indicates that ZIP64 is not required. An /// archive may use ZIP64 even when not required if the property is set to , or if the property is set to and the output stream was not /// seekable. Use the property to determine if /// the most recent Save() method resulted in an archive that utilized /// the ZIP64 extensions. /// /// /// /// /// public Nullable RequiresZip64 { get { if (_entries.Count > 65534) return new Nullable(true); // If the ZipFile has not been saved or if the contents have changed, then // it is not known if ZIP64 is required. if (!_hasBeenSaved || _contentsChanged) return null; // Whether ZIP64 is required is knowable. foreach (ZipEntry e in _entries.Values) { if (e.RequiresZip64.Value) return new Nullable(true); } return new Nullable(false); } } /// /// Indicates whether the most recent Save() operation used ZIP64 extensions. /// /// /// /// /// The use of ZIP64 extensions within an archive is not always necessary, and /// for interoperability concerns, it may be desired to NOT use ZIP64 if /// possible. The property can be /// set to use ZIP64 extensions only when necessary. In those cases, /// Sometimes applications want to know whether a Save() actually used ZIP64 /// extensions. Applications can query this read-only property to learn /// whether ZIP64 has been used in a just-saved ZipFile. /// /// /// /// The value is null (or Nothing in VB) if the archive has not /// been saved. /// /// /// /// Non-null values (HasValue is true) indicate whether ZIP64 /// extensions were used during the most recent Save() operation. The /// ZIP64 extensions may have been used as required by any particular entry /// because of its uncompressed or compressed size, or because the archive is /// larger than 4294967295 bytes, or because there are more than 65534 entries /// in the archive, or because the UseZip64WhenSaving property was set /// to , or because the /// UseZip64WhenSaving property was set to and the output stream was not seekable. /// The value of this property does not indicate the reason the ZIP64 /// extensions were used. /// /// /// /// /// public Nullable OutputUsedZip64 { get { return _OutputUsesZip64; } } /// /// Indicates whether the most recent Read() operation read a zip file that uses /// ZIP64 extensions. /// /// /// /// This property will return null (Nothing in VB) if you've added an entry after reading /// the zip file. /// public Nullable InputUsesZip64 { get { if (_entries.Count > 65534) return true; foreach (ZipEntry e in this) { // if any entry was added after reading the zip file, then the result is null if (e.Source != ZipEntrySource.ZipFile) return null; // if any entry read from the zip used zip64, then the result is true if (e._InputUsesZip64) return true; } return false; } } /// /// The text encoding to use when writing new entries to the ZipFile, /// for those entries that cannot be encoded with the default (IBM437) /// encoding; or, the text encoding that was used when reading the entries /// from the ZipFile. /// /// /// /// /// In its /// zip specification, PKWare describes two options for encoding /// filenames and comments: using IBM437 or UTF-8. But, some archiving tools /// or libraries do not follow the specification, and instead encode /// characters using the system default code page. For example, WinRAR when /// run on a machine in Shanghai may encode filenames with the Big-5 Chinese /// (950) code page. This behavior is contrary to the Zip specification, but /// it occurs anyway. /// /// /// /// When using DotNetZip to write zip archives that will be read by one of /// these other archivers, set this property to specify the code page to use /// when encoding the and for each ZipEntry in the zip file, for /// values that cannot be encoded with the default codepage for zip files, /// IBM437. This is why this property is "provisional". In all cases, IBM437 /// is used where possible, in other words, where no loss of data would /// result. It is possible, therefore, to have a given entry with a /// Comment encoded in IBM437 and a FileName encoded with the /// specified "provisional" codepage. /// /// /// /// Be aware that a zip file created after you've explicitly set the property to a value other than /// IBM437 may not be compliant to the PKWare specification, and may not be /// readable by compliant archivers. On the other hand, many (most?) /// archivers are non-compliant and can read zip files created in arbitrary /// code pages. The trick is to use or specify the proper codepage when /// reading the zip. /// /// /// /// When creating a zip archive using this library, it is possible to change /// the value of between each /// entry you add, and between adding entries and the call to /// Save(). Don't do this. It will likely result in a zipfile that is /// not readable. For best interoperability, either leave alone, or specify it only once, /// before adding any entries to the ZipFile instance. There is one /// exception to this recommendation, described later. /// /// /// /// When using an arbitrary, non-UTF8 code page for encoding, there is no /// standard way for the creator application - whether DotNetZip, WinZip, /// WinRar, or something else - to formally specify in the zip file which /// codepage has been used for the entries. As a result, readers of zip files /// are not able to inspect the zip file and determine the codepage that was /// used for the entries contained within it. It is left to the application /// or user to determine the necessary codepage when reading zip files encoded /// this way. In other words, if you explicitly specify the codepage when you /// create the zipfile, you must explicitly specify the same codepage when /// reading the zipfile. /// /// /// /// The way you specify the code page to use when reading a zip file varies /// depending on the tool or library you use to read the zip. In DotNetZip, /// you use a ZipFile.Read() method that accepts an encoding parameter. It /// isn't possible with Windows Explorer, as far as I know, to specify an /// explicit codepage to use when reading a zip. If you use an incorrect /// codepage when reading a zipfile, you will get entries with filenames that /// are incorrect, and the incorrect filenames may even contain characters /// that are not legal for use within filenames in Windows. Extracting entries /// with illegal characters in the filenames will lead to exceptions. It's too /// bad, but this is just the way things are with code pages in zip /// files. Caveat Emptor. /// /// /// /// Example: Suppose you create a zipfile that contains entries with /// filenames that have Danish characters. If you use equal to "iso-8859-1" (cp 28591), /// the filenames will be correctly encoded in the zip. But, to read that /// zipfile correctly, you have to specify the same codepage at the time you /// read it. If try to read that zip file with Windows Explorer or another /// application that is not flexible with respect to the codepage used to /// decode filenames in zipfiles, you will get a filename like "Inf°.txt". /// /// /// /// When using DotNetZip to read a zip archive, and the zip archive uses an /// arbitrary code page, you must specify the encoding to use before or when /// the Zipfile is READ. This means you must use a ZipFile.Read() /// method that allows you to specify a System.Text.Encoding parameter. Setting /// the ProvisionalAlternateEncoding property after your application has read in /// the zip archive will not affect the entry names of entries that have already /// been read in. /// /// /// /// And now, the exception to the rule described above. One strategy for /// specifying the code page for a given zip file is to describe the code page /// in a human-readable form in the Zip comment. For example, the comment may /// read "Entries in this archive are encoded in the Big5 code page". For /// maximum interoperability, the zip comment in this case should be encoded /// in the default, IBM437 code page. In this case, the zip comment is /// encoded using a different page than the filenames. To do this, Specify /// ProvisionalAlternateEncoding to your desired region-specific code /// page, once before adding any entries, and then reset /// ProvisionalAlternateEncoding to IBM437 before setting the property and calling Save(). /// /// /// /// /// This example shows how to read a zip file using the Big-5 Chinese code page /// (950), and extract each entry in the zip file. For this code to work as /// desired, the Zipfile must have been created using the big5 code page /// (CP950). This is typical, for example, when using WinRar on a machine with /// CP950 set as the default code page. In that case, the names of entries /// within the Zip archive will be stored in that code page, and reading the zip /// archive must be done using that code page. If the application did not use /// the correct code page in ZipFile.Read(), then names of entries within the /// zip archive would not be correctly retrieved. /// /// using (var zip = ZipFile.Read(zipFileName, System.Text.Encoding.GetEncoding("big5"))) /// { /// // retrieve and extract an entry using a name encoded with CP950 /// zip[MyDesiredEntry].Extract("unpack"); /// } /// /// /// /// Using zip As ZipFile = ZipFile.Read(ZipToExtract, System.Text.Encoding.GetEncoding("big5")) /// ' retrieve and extract an entry using a name encoded with CP950 /// zip(MyDesiredEntry).Extract("unpack") /// End Using /// /// /// /// DefaultEncoding [Obsolete("use AlternateEncoding instead.")] public System.Text.Encoding ProvisionalAlternateEncoding { get { if (_alternateEncodingUsage == ZipOption.AsNecessary) return _alternateEncoding; return null; } set { _alternateEncoding = value; _alternateEncodingUsage = ZipOption.AsNecessary; } } /// /// A Text Encoding to use when encoding the filenames and comments for /// all the ZipEntry items, during a ZipFile.Save() operation. /// /// /// /// Whether the encoding specified here is used during the save depends /// on . /// /// public System.Text.Encoding AlternateEncoding { get { return _alternateEncoding; } set { _alternateEncoding = value; } } /// /// A flag that tells if and when this instance should apply /// AlternateEncoding to encode the filenames and comments associated to /// of ZipEntry objects contained within this instance. /// public ZipOption AlternateEncodingUsage { get { return _alternateEncodingUsage; } set { _alternateEncodingUsage = value; } } /// /// The default text encoding used in zip archives. It is numeric 437, also /// known as IBM437. /// /// public static System.Text.Encoding DefaultEncoding { get { return _defaultEncoding; } } /// /// Gets or sets the TextWriter to which status messages are delivered /// for the instance. /// /// /// /// If the TextWriter is set to a non-null value, then verbose output is sent /// to the TextWriter during Add, Read, Save and /// Extract operations. Typically, console applications might use /// Console.Out and graphical or headless applications might use a /// System.IO.StringWriter. The output of this is suitable for viewing /// by humans. /// /// /// /// /// In this example, a console application instantiates a ZipFile, then /// sets the StatusMessageTextWriter to Console.Out. At that /// point, all verbose status messages for that ZipFile are sent to the /// console. /// /// /// /// using (ZipFile zip= ZipFile.Read(FilePath)) /// { /// zip.StatusMessageTextWriter= System.Console.Out; /// // messages are sent to the console during extraction /// zip.ExtractAll(); /// } /// /// /// /// Using zip As ZipFile = ZipFile.Read(FilePath) /// zip.StatusMessageTextWriter= System.Console.Out /// 'Status Messages will be sent to the console during extraction /// zip.ExtractAll() /// End Using /// /// /// /// In this example, a Windows Forms application instantiates a /// ZipFile, then sets the StatusMessageTextWriter to a /// StringWriter. At that point, all verbose status messages for that /// ZipFile are sent to the StringWriter. /// /// /// /// var sw = new System.IO.StringWriter(); /// using (ZipFile zip= ZipFile.Read(FilePath)) /// { /// zip.StatusMessageTextWriter= sw; /// zip.ExtractAll(); /// } /// Console.WriteLine("{0}", sw.ToString()); /// /// /// /// Dim sw as New System.IO.StringWriter /// Using zip As ZipFile = ZipFile.Read(FilePath) /// zip.StatusMessageTextWriter= sw /// zip.ExtractAll() /// End Using /// 'Status Messages are now available in sw /// /// /// public TextWriter StatusMessageTextWriter { get { return _StatusMessageTextWriter; } set { _StatusMessageTextWriter = value; } } /// /// Gets or sets the name for the folder to store the temporary file /// this library writes when saving a zip archive. /// /// /// /// /// This library will create a temporary file when saving a Zip archive to a /// file. This file is written when calling one of the Save() methods /// that does not save to a stream, or one of the SaveSelfExtractor() /// methods. /// /// /// /// By default, the library will create the temporary file in the directory /// specified for the file itself, via the property or via /// the method. /// /// /// /// Setting this property allows applications to override this default /// behavior, so that the library will create the temporary file in the /// specified folder. For example, to have the library create the temporary /// file in the current working directory, regardless where the ZipFile /// is saved, specfy ".". To revert to the default behavior, set this /// property to null (Nothing in VB). /// /// /// /// When setting the property to a non-null value, the folder specified must /// exist; if it does not an exception is thrown. The application should have /// write and delete permissions on the folder. The permissions are not /// explicitly checked ahead of time; if the application does not have the /// appropriate rights, an exception will be thrown at the time Save() /// is called. /// /// /// /// There is no temporary file created when reading a zip archive. When /// saving to a Stream, there is no temporary file created. For example, if /// the application is an ASP.NET application and calls Save() /// specifying the Response.OutputStream as the output stream, there is /// no temporary file created. /// /// /// /// /// Thrown when setting the property if the directory does not exist. /// /// public String TempFileFolder { get { return _TempFileFolder; } set { _TempFileFolder = value; if (value == null) return; if (!Directory.Exists(value)) throw new FileNotFoundException(String.Format("That directory ({0}) does not exist.", value)); } } /// /// Sets the password to be used on the ZipFile instance. /// /// /// /// /// /// When writing a zip archive, this password is applied to the entries, not /// to the zip archive itself. It applies to any ZipEntry subsequently /// added to the ZipFile, using one of the AddFile, /// AddDirectory, AddEntry, or AddItem methods, etc. /// When reading a zip archive, this property applies to any entry /// subsequently extracted from the ZipFile using one of the Extract /// methods on the ZipFile class. /// /// /// /// When writing a zip archive, keep this in mind: though the password is set /// on the ZipFile object, according to the Zip spec, the "directory" of the /// archive - in other words the list of entries or files contained in the archive - is /// not encrypted with the password, or protected in any way. If you set the /// Password property, the password actually applies to individual entries /// that are added to the archive, subsequent to the setting of this property. /// The list of filenames in the archive that is eventually created will /// appear in clear text, but the contents of the individual files are /// encrypted. This is how Zip encryption works. /// /// /// /// One simple way around this limitation is to simply double-wrap sensitive /// filenames: Store the files in a zip file, and then store that zip file /// within a second, "outer" zip file. If you apply a password to the outer /// zip file, then readers will be able to see that the outer zip file /// contains an inner zip file. But readers will not be able to read the /// directory or file list of the inner zip file. /// /// /// /// If you set the password on the ZipFile, and then add a set of files /// to the archive, then each entry is encrypted with that password. You may /// also want to change the password between adding different entries. If you /// set the password, add an entry, then set the password to null /// (Nothing in VB), and add another entry, the first entry is /// encrypted and the second is not. If you call AddFile(), then set /// the Password property, then call ZipFile.Save, the file /// added will not be password-protected, and no warning will be generated. /// /// /// /// When setting the Password, you may also want to explicitly set the property, to specify how to encrypt the entries added /// to the ZipFile. If you set the Password to a non-null value and do not /// set , then PKZip 2.0 ("Weak") encryption is used. /// This encryption is relatively weak but is very interoperable. If you set /// the password to a null value (Nothing in VB), Encryption is /// reset to None. /// /// /// /// All of the preceding applies to writing zip archives, in other words when /// you use one of the Save methods. To use this property when reading or an /// existing ZipFile, do the following: set the Password property on the /// ZipFile, then call one of the Extract() overloads on the . In this case, the entry is extracted using the /// Password that is specified on the ZipFile instance. If you /// have not set the Password property, then the password is /// null, and the entry is extracted with no password. /// /// /// /// If you set the Password property on the ZipFile, then call /// Extract() an entry that has not been encrypted with a password, the /// password is not used for that entry, and the ZipEntry is extracted /// as normal. In other words, the password is used only if necessary. /// /// /// /// The class also has a Password property. It takes precedence /// over this property on the ZipFile. Typically, you would use the /// per-entry Password when most entries in the zip archive use one password, /// and a few entries use a different password. If all entries in the zip /// file use the same password, then it is simpler to just set this property /// on the ZipFile itself, whether creating a zip archive or extracting /// a zip archive. /// /// /// /// /// /// /// This example creates a zip file, using password protection for the /// entries, and then extracts the entries from the zip file. When creating /// the zip file, the Readme.txt file is not protected with a password, but /// the other two are password-protected as they are saved. During extraction, /// each file is extracted with the appropriate password. /// /// /// // create a file with encryption /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddFile("ReadMe.txt"); /// zip.Password= "!Secret1"; /// zip.AddFile("MapToTheSite-7440-N49th.png"); /// zip.AddFile("2008-Regional-Sales-Report.pdf"); /// zip.Save("EncryptedArchive.zip"); /// } /// /// // extract entries that use encryption /// using (ZipFile zip = ZipFile.Read("EncryptedArchive.zip")) /// { /// zip.Password= "!Secret1"; /// zip.ExtractAll("extractDir"); /// } /// /// /// /// /// Using zip As New ZipFile /// zip.AddFile("ReadMe.txt") /// zip.Password = "123456!" /// zip.AddFile("MapToTheSite-7440-N49th.png") /// zip.Password= "!Secret1"; /// zip.AddFile("2008-Regional-Sales-Report.pdf") /// zip.Save("EncryptedArchive.zip") /// End Using /// /// /// ' extract entries that use encryption /// Using (zip as ZipFile = ZipFile.Read("EncryptedArchive.zip")) /// zip.Password= "!Secret1" /// zip.ExtractAll("extractDir") /// End Using /// /// /// /// /// /// ZipFile.Encryption /// ZipEntry.Password public String Password { set { _Password = value; if (_Password == null) { Encryption = EncryptionAlgorithm.None; } else if (Encryption == EncryptionAlgorithm.None) { Encryption = EncryptionAlgorithm.PkzipWeak; } } private get { return _Password; } } /// /// The action the library should take when extracting a file that already /// exists. /// /// /// /// /// This property affects the behavior of the Extract methods (one of the /// Extract() or ExtractWithPassword() overloads), when /// extraction would would overwrite an existing filesystem file. If you do /// not set this property, the library throws an exception when extracting an /// entry would overwrite an existing file. /// /// /// /// This property has no effect when extracting to a stream, or when the file /// to be extracted does not already exist. /// /// /// public ExtractExistingFileAction ExtractExistingFile { get; set; } /// /// The action the library should take when an error is encountered while /// opening or reading files as they are saved into a zip archive. /// /// /// /// /// Errors can occur as a file is being saved to the zip archive. For /// example, the File.Open may fail, or a File.Read may fail, because of /// lock conflicts or other reasons. /// /// /// /// The first problem might occur after having called AddDirectory() on a /// directory that contains a Clipper .dbf file; the file is locked by /// Clipper and cannot be opened for read by another process. An example of /// the second problem might occur when trying to zip a .pst file that is in /// use by Microsoft Outlook. Outlook locks a range on the file, which allows /// other processes to open the file, but not read it in its entirety. /// /// /// /// This property tells DotNetZip what you would like to do in the case of /// these errors. The primary options are: ZipErrorAction.Throw to /// throw an exception (this is the default behavior if you don't set this /// property); ZipErrorAction.Skip to Skip the file for which there /// was an error and continue saving; ZipErrorAction.Retry to Retry /// the entry that caused the problem; or /// ZipErrorAction.InvokeErrorEvent to invoke an event handler. /// /// /// /// This property is implicitly set to ZipErrorAction.InvokeErrorEvent /// if you add a handler to the event. If you set /// this property to something other than /// ZipErrorAction.InvokeErrorEvent, then the ZipError /// event is implicitly cleared. What it means is you can set one or the /// other (or neither), depending on what you want, but you never need to set /// both. /// /// /// /// As with some other properties on the ZipFile class, like , , and , setting this property on a ZipFile /// instance will cause the specified ZipErrorAction to be used on all /// items that are subsequently added to the /// ZipFile instance. If you set this property after you have added /// items to the ZipFile, but before you have called Save(), /// those items will not use the specified error handling action. /// /// /// /// If you want to handle any errors that occur with any entry in the zip /// file in the same way, then set this property once, before adding any /// entries to the zip archive. /// /// /// /// If you set this property to ZipErrorAction.Skip and you'd like to /// learn which files may have been skipped after a Save(), you can /// set the on the ZipFile before /// calling Save(). A message will be emitted into that writer for /// each skipped file, if any. /// /// /// /// /// /// This example shows how to tell DotNetZip to skip any files for which an /// error is generated during the Save(). /// /// Public Sub SaveZipFile() /// Dim SourceFolder As String = "fodder" /// Dim DestFile As String = "eHandler.zip" /// Dim sw as New StringWriter /// Using zipArchive As ZipFile = New ZipFile /// ' Tell DotNetZip to skip any files for which it encounters an error /// zipArchive.ZipErrorAction = ZipErrorAction.Skip /// zipArchive.StatusMessageTextWriter = sw /// zipArchive.AddDirectory(SourceFolder) /// zipArchive.Save(DestFile) /// End Using /// ' examine sw here to see any messages /// End Sub /// /// /// /// /// /// public ZipErrorAction ZipErrorAction { get { if (ZipError != null) _zipErrorAction = ZipErrorAction.InvokeErrorEvent; return _zipErrorAction; } set { _zipErrorAction = value; if (_zipErrorAction != ZipErrorAction.InvokeErrorEvent && ZipError != null) ZipError = null; } } /// /// The Encryption to use for entries added to the ZipFile. /// /// /// /// /// Set this when creating a zip archive, or when updating a zip archive. The /// specified Encryption is applied to the entries subsequently added to the /// ZipFile instance. Applications do not need to set the /// Encryption property when reading or extracting a zip archive. /// /// /// /// If you set this to something other than EncryptionAlgorithm.None, you /// will also need to set the . /// /// /// /// As with some other properties on the ZipFile class, like and , setting this /// property on a ZipFile instance will cause the specified /// EncryptionAlgorithm to be used on all items /// that are subsequently added to the ZipFile instance. In other /// words, if you set this property after you have added items to the /// ZipFile, but before you have called Save(), those items will /// not be encrypted or protected with a password in the resulting zip /// archive. To get a zip archive with encrypted entries, set this property, /// along with the property, before calling /// AddFile, AddItem, or AddDirectory (etc.) on the /// ZipFile instance. /// /// /// /// If you read a ZipFile, you can modify the Encryption on an /// encrypted entry, only by setting the Encryption property on the /// ZipEntry itself. Setting the Encryption property on the /// ZipFile, once it has been created via a call to /// ZipFile.Read(), does not affect entries that were previously read. /// /// /// /// For example, suppose you read a ZipFile, and there is an encrypted /// entry. Setting the Encryption property on that ZipFile and /// then calling Save() on the ZipFile does not update the /// Encryption used for the entries in the archive. Neither is an /// exception thrown. Instead, what happens during the Save() is that /// all previously existing entries are copied through to the new zip archive, /// with whatever encryption and password that was used when originally /// creating the zip archive. Upon re-reading that archive, to extract /// entries, applications should use the original password or passwords, if /// any. /// /// /// /// Suppose an application reads a ZipFile, and there is an encrypted /// entry. Setting the Encryption property on that ZipFile and /// then adding new entries (via AddFile(), AddEntry(), etc) /// and then calling Save() on the ZipFile does not update the /// Encryption on any of the entries that had previously been in the /// ZipFile. The Encryption property applies only to the /// newly-added entries. /// /// /// /// /// /// /// This example creates a zip archive that uses encryption, and then extracts /// entries from the archive. When creating the zip archive, the ReadMe.txt /// file is zipped without using a password or encryption. The other files /// use encryption. /// /// /// /// // Create a zip archive with AES Encryption. /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddFile("ReadMe.txt"); /// zip.Encryption= EncryptionAlgorithm.WinZipAes256; /// zip.Password= "Top.Secret.No.Peeking!"; /// zip.AddFile("7440-N49th.png"); /// zip.AddFile("2008-Regional-Sales-Report.pdf"); /// zip.Save("EncryptedArchive.zip"); /// } /// /// // Extract a zip archive that uses AES Encryption. /// // You do not need to specify the algorithm during extraction. /// using (ZipFile zip = ZipFile.Read("EncryptedArchive.zip")) /// { /// zip.Password= "Top.Secret.No.Peeking!"; /// zip.ExtractAll("extractDirectory"); /// } /// /// /// /// ' Create a zip that uses Encryption. /// Using zip As New ZipFile() /// zip.Encryption= EncryptionAlgorithm.WinZipAes256 /// zip.Password= "Top.Secret.No.Peeking!" /// zip.AddFile("ReadMe.txt") /// zip.AddFile("7440-N49th.png") /// zip.AddFile("2008-Regional-Sales-Report.pdf") /// zip.Save("EncryptedArchive.zip") /// End Using /// /// ' Extract a zip archive that uses AES Encryption. /// ' You do not need to specify the algorithm during extraction. /// Using (zip as ZipFile = ZipFile.Read("EncryptedArchive.zip")) /// zip.Password= "Top.Secret.No.Peeking!" /// zip.ExtractAll("extractDirectory") /// End Using /// /// /// /// /// ZipFile.Password /// ZipEntry.Encryption public EncryptionAlgorithm Encryption { get { return _Encryption; } set { if (value == EncryptionAlgorithm.Unsupported) throw new InvalidOperationException("You may not set Encryption to that value."); _Encryption = value; } } /// /// A callback that allows the application to specify the compression level /// to use for entries subsequently added to the zip archive. /// /// /// /// /// /// With this callback, the DotNetZip library allows the application to /// determine whether compression will be used, at the time of the /// Save. This may be useful if the application wants to favor /// speed over size, and wants to defer the decision until the time of /// Save. /// /// /// /// Typically applications set the property on /// the ZipFile or on each ZipEntry to determine the level of /// compression used. This is done at the time the entry is added to the /// ZipFile. Setting the property to /// Ionic.Zlib.CompressionLevel.None means no compression will be used. /// /// /// /// This callback allows the application to defer the decision on the /// CompressionLevel to use, until the time of the call to /// ZipFile.Save(). The callback is invoked once per ZipEntry, /// at the time the data for the entry is being written out as part of a /// Save() operation. The application can use whatever criteria it /// likes in determining the level to return. For example, an application may /// wish that no .mp3 files should be compressed, because they are already /// compressed and the extra compression is not worth the CPU time incurred, /// and so can return None for all .mp3 entries. /// /// /// /// The library determines whether compression will be attempted for an entry /// this way: If the entry is a zero length file, or a directory, no /// compression is used. Otherwise, if this callback is set, it is invoked /// and the CompressionLevel is set to the return value. If this /// callback has not been set, then the previously set value for /// CompressionLevel is used. /// /// /// public SetCompressionCallback SetCompression { get; set; } /// /// The maximum size of an output segment, when saving a split Zip file. /// /// /// /// Set this to a non-zero value before calling or to specify that the ZipFile should be saved as a /// split archive, also sometimes called a spanned archive. Some also /// call them multi-file archives. /// /// /// /// A split zip archive is saved in a set of discrete filesystem files, /// rather than in a single file. This is handy when transmitting the /// archive in email or some other mechanism that has a limit to the size of /// each file. The first file in a split archive will be named /// basename.z01, the second will be named basename.z02, and /// so on. The final file is named basename.zip. According to the zip /// specification from PKWare, the minimum value is 65536, for a 64k segment /// size. The maximum number of segments allows in a split archive is 99. /// /// /// /// The value of this property determines the maximum size of a split /// segment when writing a split archive. For example, suppose you have a /// ZipFile that would save to a single file of 200k. If you set the /// MaxOutputSegmentSize to 65536 before calling Save(), you /// will get four distinct output files. On the other hand if you set this /// property to 256k, then you will get a single-file archive for that /// ZipFile. /// /// /// /// The size of each split output file will be as large as possible, up to /// the maximum size set here. The zip specification requires that some data /// fields in a zip archive may not span a split boundary, and an output /// segment may be smaller than the maximum if necessary to avoid that /// problem. Also, obviously the final segment of the archive may be smaller /// than the maximum segment size. Segments will never be larger than the /// value set with this property. /// /// /// /// You can save a split Zip file only when saving to a regular filesystem /// file. It's not possible to save a split zip file as a self-extracting /// archive, nor is it possible to save a split zip file to a stream. When /// saving to a SFX or to a Stream, this property is ignored. /// /// /// /// About interoperability: Split or spanned zip files produced by DotNetZip /// can be read by WinZip or PKZip, and vice-versa. Segmented zip files may /// not be readable by other tools, if those other tools don't support zip /// spanning or splitting. When in doubt, test. I don't believe Windows /// Explorer can extract a split archive. /// /// /// /// This property has no effect when reading a split archive. You can read /// a split archive in the normal way with DotNetZip. /// /// /// /// When saving a zip file, if you want a regular zip file rather than a /// split zip file, don't set this property, or set it to Zero. /// /// /// /// If you read a split archive, with and /// then subsequently call ZipFile.Save(), unless you set this /// property before calling Save(), you will get a normal, /// single-file archive. /// /// /// /// public Int32 MaxOutputSegmentSize { get { return _maxOutputSegmentSize; } set { if (value < 65536 && value != 0) throw new ZipException("The minimum acceptable segment size is 65536."); _maxOutputSegmentSize = value; } } /// /// Returns the number of segments used in the most recent Save() operation. /// /// /// /// This is normally zero, unless you have set the property. If you have set , and then you save a file, after the call to /// Save() completes, you can read this value to learn the number of segments that /// were created. /// /// /// If you call Save("Archive.zip"), and it creates 5 segments, then you /// will have filesystem files named Archive.z01, Archive.z02, Archive.z03, /// Archive.z04, and Archive.zip, and the value of this property will be 5. /// /// /// public Int32 NumberOfSegmentsForMostRecentSave { get { return unchecked((Int32)_numberOfSegmentsForMostRecentSave + 1); } } #if !NETCF /// /// The size threshold for an entry, above which a parallel deflate is used. /// /// /// /// /// /// DotNetZip will use multiple threads to compress any ZipEntry, /// if the entry is larger than the given size. Zero means "always /// use parallel deflate", while -1 means "never use parallel /// deflate". The default value for this property is 512k. Aside /// from the special values of 0 and 1, the minimum value is 65536. /// /// /// /// If the entry size cannot be known before compression, as with a /// read-forward stream, then Parallel deflate will never be /// performed, unless the value of this property is zero. /// /// /// /// A parallel deflate operations will speed up the compression of /// large files, on computers with multiple CPUs or multiple CPU /// cores. For files above 1mb, on a dual core or dual-cpu (2p) /// machine, the time required to compress the file can be 70% of the /// single-threaded deflate. For very large files on 4p machines the /// compression can be done in 30% of the normal time. The downside /// is that parallel deflate consumes extra memory during the deflate, /// and the deflation is not as effective. /// /// /// /// Parallel deflate tends to yield slightly less compression when /// compared to as single-threaded deflate; this is because the original /// data stream is split into multiple independent buffers, each of which /// is compressed in parallel. But because they are treated /// independently, there is no opportunity to share compression /// dictionaries. For that reason, a deflated stream may be slightly /// larger when compressed using parallel deflate, as compared to a /// traditional single-threaded deflate. Sometimes the increase over the /// normal deflate is as much as 5% of the total compressed size. For /// larger files it can be as small as 0.1%. /// /// /// /// Multi-threaded compression does not give as much an advantage when /// using Encryption. This is primarily because encryption tends to slow /// down the entire pipeline. Also, multi-threaded compression gives less /// of an advantage when using lower compression levels, for example . You may have to /// perform some tests to determine the best approach for your situation. /// /// /// /// /// /// public long ParallelDeflateThreshold { set { if ((value != 0) && (value != -1) && (value < 64 * 1024)) throw new ArgumentOutOfRangeException("ParallelDeflateThreshold should be -1, 0, or > 65536"); _ParallelDeflateThreshold = value; } get { return _ParallelDeflateThreshold; } } /// /// The maximum number of buffer pairs to use when performing /// parallel compression. /// /// /// /// /// This property sets an upper limit on the number of memory /// buffer pairs to create when performing parallel /// compression. The implementation of the parallel /// compression stream allocates multiple buffers to /// facilitate parallel compression. As each buffer fills up, /// the stream uses /// ThreadPool.QueueUserWorkItem() to compress those /// buffers in a background threadpool thread. After a buffer /// is compressed, it is re-ordered and written to the output /// stream. /// /// /// /// A higher number of buffer pairs enables a higher degree of /// parallelism, which tends to increase the speed of compression on /// multi-cpu computers. On the other hand, a higher number of buffer /// pairs also implies a larger memory consumption, more active worker /// threads, and a higher cpu utilization for any compression. This /// property enables the application to limit its memory consumption and /// CPU utilization behavior depending on requirements. /// /// /// /// For each compression "task" that occurs in parallel, there are 2 /// buffers allocated: one for input and one for output. This property /// sets a limit for the number of pairs. The total amount of storage /// space allocated for buffering will then be (N*S*2), where N is the /// number of buffer pairs, S is the size of each buffer (). By default, DotNetZip allocates 4 buffer /// pairs per CPU core, so if your machine has 4 cores, and you retain /// the default buffer size of 128k, then the /// ParallelDeflateOutputStream will use 4 * 4 * 2 * 128kb of buffer /// memory in total, or 4mb, in blocks of 128kb. If you then set this /// property to 8, then the number will be 8 * 2 * 128kb of buffer /// memory, or 2mb. /// /// /// /// CPU utilization will also go up with additional buffers, because a /// larger number of buffer pairs allows a larger number of background /// threads to compress in parallel. If you find that parallel /// compression is consuming too much memory or CPU, you can adjust this /// value downward. /// /// /// /// The default value is 16. Different values may deliver better or /// worse results, depending on your priorities and the dynamic /// performance characteristics of your storage and compute resources. /// /// /// /// This property is not the number of buffer pairs to use; it is an /// upper limit. An illustration: Suppose you have an application that /// uses the default value of this property (which is 16), and it runs /// on a machine with 2 CPU cores. In that case, DotNetZip will allocate /// 4 buffer pairs per CPU core, for a total of 8 pairs. The upper /// limit specified by this property has no effect. /// /// /// /// The application can set this value at any time /// before calling ZipFile.Save(). /// /// /// /// /// public int ParallelDeflateMaxBufferPairs { get { return _maxBufferPairs; } set { if (value < 4) throw new ArgumentOutOfRangeException("ParallelDeflateMaxBufferPairs", "Value must be 4 or greater."); _maxBufferPairs = value; } } #endif /// Provides a string representation of the instance. /// a string representation of the instance. public override String ToString() { return String.Format("ZipFile::{0}", Name); } /// /// Returns the version number on the DotNetZip assembly. /// /// /// /// /// This property is exposed as a convenience. Callers could also get the /// version value by retrieving GetName().Version on the /// System.Reflection.Assembly object pointing to the DotNetZip /// assembly. But sometimes it is not clear which assembly is being loaded. /// This property makes it clear. /// /// /// This static property is primarily useful for diagnostic purposes. /// /// public static System.Version LibraryVersion { get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; } } internal void NotifyEntryChanged() { _contentsChanged = true; } internal Stream StreamForDiskNumber(uint diskNumber) { if (diskNumber + 1 == this._diskNumberWithCd || (diskNumber == 0 && this._diskNumberWithCd == 0)) { //return (this.ReadStream as FileStream); return this.ReadStream; } return ZipSegmentedStream.ForReading(this._readName ?? this._name, diskNumber, _diskNumberWithCd); } // called by ZipEntry in ZipEntry.Extract(), when there is no stream set for the // ZipEntry. internal void Reset(bool whileSaving) { if (_JustSaved) { // read in the just-saved zip archive using (ZipFile x = new ZipFile()) { // workitem 10735 x._readName = x._name = whileSaving ? (this._readName ?? this._name) : this._name; x.AlternateEncoding = this.AlternateEncoding; x.AlternateEncodingUsage = this.AlternateEncodingUsage; ReadIntoInstance(x); // copy the contents of the entries. // cannot just replace the entries - the app may be holding them foreach (ZipEntry e1 in x) { foreach (ZipEntry e2 in this) { if (e1.FileName == e2.FileName) { e2.CopyMetaData(e1); break; } } } } _JustSaved = false; } } #endregion #region Constructors /// /// Creates a new ZipFile instance, using the specified filename. /// /// /// /// /// Applications can use this constructor to create a new ZipFile for writing, /// or to slurp in an existing zip archive for read and update purposes. /// /// /// /// To create a new zip archive, an application can call this constructor, /// passing the name of a file that does not exist. The name may be a fully /// qualified path. Then the application can add directories or files to the /// ZipFile via AddDirectory(), AddFile(), AddItem() /// and then write the zip archive to the disk by calling Save(). The /// zip file is not actually opened and written to the disk until the /// application calls ZipFile.Save(). At that point the new zip file /// with the given name is created. /// /// /// /// If you won't know the name of the Zipfile until the time you call /// ZipFile.Save(), or if you plan to save to a stream (which has no /// name), then you should use the no-argument constructor. /// /// /// /// The application can also call this constructor to read an existing zip /// archive. passing the name of a valid zip file that does exist. But, it's /// better form to use the static method, /// passing the name of the zip file, because using ZipFile.Read() in /// your code communicates very clearly what you are doing. In either case, /// the file is then read into the ZipFile instance. The app can then /// enumerate the entries or can modify the zip file, for example adding /// entries, removing entries, changing comments, and so on. /// /// /// /// One advantage to this parameterized constructor: it allows applications to /// use the same code to add items to a zip archive, regardless of whether the /// zip file exists. /// /// /// /// Instances of the ZipFile class are not multi-thread safe. You may /// not party on a single instance with multiple threads. You may have /// multiple threads that each use a distinct ZipFile instance, or you /// can synchronize multi-thread access to a single instance. /// /// /// /// By the way, since DotNetZip is so easy to use, don't you think you should /// donate $5 or $10? /// /// /// /// /// /// Thrown if name refers to an existing file that is not a valid zip file. /// /// /// /// This example shows how to create a zipfile, 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, "files"); /// zip.Save(ZipFileToCreate); /// } /// /// /// /// Dim ZipFileToCreate As String = "archive1.zip" /// Dim DirectoryToZip As String = "c:\reports" /// Using zip As ZipFile = New ZipFile() /// Dim filenames As String() = System.IO.Directory.GetFiles(DirectoryToZip) /// zip.AddFiles(filenames, "files") /// zip.Save(ZipFileToCreate) /// End Using /// /// /// /// The filename to use for the new zip archive. /// public ZipFile(string fileName) { try { _InitInstance(fileName, null); } catch (Exception e1) { throw new ZipException(String.Format("Could not read {0} as a zip file", fileName), e1); } } /// /// Creates a new ZipFile instance, using the specified name for the /// filename, and the specified Encoding. /// /// /// /// /// See the documentation on the ZipFile /// constructor that accepts a single string argument for basic /// information on all the ZipFile constructors. /// /// /// /// The Encoding is used as the default alternate encoding for entries with /// filenames or comments that cannot be encoded with the IBM437 code page. /// This is equivalent to setting the property on the ZipFile /// instance after construction. /// /// /// /// Instances of the ZipFile class are not multi-thread safe. You may /// not party on a single instance with multiple threads. You may have /// multiple threads that each use a distinct ZipFile instance, or you /// can synchronize multi-thread access to a single instance. /// /// /// /// /// /// Thrown if name refers to an existing file that is not a valid zip file. /// /// /// The filename to use for the new zip archive. /// The Encoding is used as the default alternate /// encoding for entries with filenames or comments that cannot be encoded /// with the IBM437 code page. public ZipFile(string fileName, System.Text.Encoding encoding) { try { AlternateEncoding = encoding; AlternateEncodingUsage = ZipOption.Always; _InitInstance(fileName, null); } catch (Exception e1) { throw new ZipException(String.Format("{0} is not a valid zip file", fileName), e1); } } /// /// Create a zip file, without specifying a target filename or stream to save to. /// /// /// /// /// See the documentation on the ZipFile /// constructor that accepts a single string argument for basic /// information on all the ZipFile constructors. /// /// /// /// After instantiating with this constructor and adding entries to the /// archive, the application should call or /// to save to a file or a /// stream, respectively. The application can also set the /// property and then call the no-argument method. (This /// is the preferred approach for applications that use the library through /// COM interop.) If you call the no-argument method /// without having set the Name of the ZipFile, either through /// the parameterized constructor or through the explicit property , the /// Save() will throw, because there is no place to save the file. /// /// /// Instances of the ZipFile class are not multi-thread safe. You may /// have multiple threads that each use a distinct ZipFile instance, or /// you can synchronize multi-thread access to a single instance. /// /// /// /// /// This example creates a Zip archive called Backup.zip, containing all the files /// in the directory DirectoryToZip. Files within subdirectories are not zipped up. /// /// using (ZipFile zip = new ZipFile()) /// { /// // Store all files found in the top level directory, into the zip archive. /// // note: this code does not recurse subdirectories! /// String[] filenames = System.IO.Directory.GetFiles(DirectoryToZip); /// zip.AddFiles(filenames, "files"); /// zip.Save("Backup.zip"); /// } /// /// /// /// Using zip As New ZipFile /// ' Store all files found in the top level directory, into the zip archive. /// ' note: this code does not recurse subdirectories! /// Dim filenames As String() = System.IO.Directory.GetFiles(DirectoryToZip) /// zip.AddFiles(filenames, "files") /// zip.Save("Backup.zip") /// End Using /// /// public ZipFile() { _InitInstance(null, null); } /// /// Create a zip file, specifying a text Encoding, but without specifying a /// target filename or stream to save to. /// /// /// /// /// See the documentation on the ZipFile /// constructor that accepts a single string argument for basic /// information on all the ZipFile constructors. /// /// /// /// /// /// The Encoding is used as the default alternate encoding for entries with /// filenames or comments that cannot be encoded with the IBM437 code page. /// public ZipFile(System.Text.Encoding encoding) { AlternateEncoding = encoding; AlternateEncodingUsage = ZipOption.Always; _InitInstance(null, null); } /// /// Creates a new ZipFile instance, using the specified name for the /// filename, and the specified status message writer. /// /// /// /// /// See the documentation on the ZipFile /// constructor that accepts a single string argument for basic /// information on all the ZipFile constructors. /// /// /// /// This version of the constructor allows the caller to pass in a TextWriter, /// to which verbose messages will be written during extraction or creation of /// the zip archive. A console application may wish to pass /// System.Console.Out to get messages on the Console. A graphical or headless /// application may wish to capture the messages in a different /// TextWriter, for example, a StringWriter, and then display /// the messages in a TextBox, or generate an audit log of ZipFile operations. /// /// /// /// To encrypt the data for the files added to the ZipFile instance, /// set the Password property after creating the ZipFile instance. /// /// /// /// Instances of the ZipFile class are not multi-thread safe. You may /// not party on a single instance with multiple threads. You may have /// multiple threads that each use a distinct ZipFile instance, or you /// can synchronize multi-thread access to a single instance. /// /// /// /// /// /// Thrown if name refers to an existing file that is not a valid zip file. /// /// /// /// /// using (ZipFile zip = new ZipFile("Backup.zip", Console.Out)) /// { /// // Store all files found in the top level directory, into the zip archive. /// // note: this code does not recurse subdirectories! /// // Status messages will be written to Console.Out /// String[] filenames = System.IO.Directory.GetFiles(DirectoryToZip); /// zip.AddFiles(filenames); /// zip.Save(); /// } /// /// /// /// Using zip As New ZipFile("Backup.zip", Console.Out) /// ' Store all files found in the top level directory, into the zip archive. /// ' note: this code does not recurse subdirectories! /// ' Status messages will be written to Console.Out /// Dim filenames As String() = System.IO.Directory.GetFiles(DirectoryToZip) /// zip.AddFiles(filenames) /// zip.Save() /// End Using /// /// /// /// The filename to use for the new zip archive. /// A TextWriter to use for writing /// verbose status messages. public ZipFile(string fileName, TextWriter statusMessageWriter) { try { _InitInstance(fileName, statusMessageWriter); } catch (Exception e1) { throw new ZipException(String.Format("{0} is not a valid zip file", fileName), e1); } } /// /// Creates a new ZipFile instance, using the specified name for the /// filename, the specified status message writer, and the specified Encoding. /// /// /// /// /// This constructor works like the ZipFile /// constructor that accepts a single string argument. See that /// reference for detail on what this constructor does. /// /// /// /// This version of the constructor allows the caller to pass in a /// TextWriter, and an Encoding. The TextWriter will collect /// verbose messages that are generated by the library during extraction or /// creation of the zip archive. A console application may wish to pass /// System.Console.Out to get messages on the Console. A graphical or /// headless application may wish to capture the messages in a different /// TextWriter, for example, a StringWriter, and then display /// the messages in a TextBox, or generate an audit log of /// ZipFile operations. /// /// /// /// The Encoding is used as the default alternate encoding for entries /// with filenames or comments that cannot be encoded with the IBM437 code /// page. This is a equivalent to setting the property on the ZipFile /// instance after construction. /// /// /// /// To encrypt the data for the files added to the ZipFile instance, /// set the Password property after creating the ZipFile /// instance. /// /// /// /// Instances of the ZipFile class are not multi-thread safe. You may /// not party on a single instance with multiple threads. You may have /// multiple threads that each use a distinct ZipFile instance, or you /// can synchronize multi-thread access to a single instance. /// /// /// /// /// /// Thrown if fileName refers to an existing file that is not a valid zip file. /// /// /// The filename to use for the new zip archive. /// A TextWriter to use for writing verbose /// status messages. /// /// The Encoding is used as the default alternate encoding for entries with /// filenames or comments that cannot be encoded with the IBM437 code page. /// public ZipFile(string fileName, TextWriter statusMessageWriter, System.Text.Encoding encoding) { try { AlternateEncoding = encoding; AlternateEncodingUsage = ZipOption.Always; _InitInstance(fileName, statusMessageWriter); } catch (Exception e1) { throw new ZipException(String.Format("{0} is not a valid zip file", fileName), e1); } } /// /// Initialize a ZipFile instance by reading in a zip file. /// /// /// /// /// /// This method is primarily useful from COM Automation environments, when /// reading or extracting zip files. In COM, it is not possible to invoke /// parameterized constructors for a class. A COM Automation application can /// update a zip file by using the default (no argument) /// constructor, then calling Initialize() to read the contents /// of an on-disk zip archive into the ZipFile instance. /// /// /// /// .NET applications are encouraged to use the ZipFile.Read() methods /// for better clarity. /// /// /// /// the name of the existing zip file to read in. public void Initialize(string fileName) { try { _InitInstance(fileName, null); } catch (Exception e1) { throw new ZipException(String.Format("{0} is not a valid zip file", fileName), e1); } } private void _initEntriesDictionary() { // workitem 9868 StringComparer sc = (CaseSensitiveRetrieval) ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; _entries = (_entries == null) ? new Dictionary(sc) : new Dictionary(_entries, sc); } private void _InitInstance(string zipFileName, TextWriter statusMessageWriter) { // create a new zipfile _name = zipFileName; _StatusMessageTextWriter = statusMessageWriter; _contentsChanged = true; AddDirectoryWillTraverseReparsePoints = true; // workitem 8617 CompressionLevel = Ionic.Zlib.CompressionLevel.Default; #if !NETCF ParallelDeflateThreshold = 512 * 1024; #endif // workitem 7685, 9868 _initEntriesDictionary(); if (File.Exists(_name)) { if (FullScan) ReadIntoInstance_Orig(this); else ReadIntoInstance(this); this._fileAlreadyExists = true; } return; } #endregion #region Indexers and Collections private List ZipEntriesAsList { get { if (_zipEntriesAsList == null) _zipEntriesAsList = new List(_entries.Values); return _zipEntriesAsList; } } /// /// This is an integer indexer into the Zip archive. /// /// /// /// /// This property is read-only. /// /// /// /// Internally, the ZipEntry instances that belong to the /// ZipFile are stored in a Dictionary. When you use this /// indexer the first time, it creates a read-only /// List<ZipEntry> from the Dictionary.Values Collection. /// If at any time you modify the set of entries in the ZipFile, /// either by adding an entry, removing an entry, or renaming an /// entry, a new List will be created, and the numeric indexes for the /// remaining entries may be different. /// /// /// /// This means you cannot rename any ZipEntry from /// inside an enumeration of the zip file. /// /// /// /// The index value. /// /// /// /// /// /// The ZipEntry within the Zip archive at the specified index. If the /// entry does not exist in the archive, this indexer throws. /// /// public ZipEntry this[int ix] { // workitem 6402 get { return ZipEntriesAsList[ix]; } } /// /// This is a name-based indexer into the Zip archive. /// /// /// /// /// This property is read-only. /// /// /// /// The property on the ZipFile /// determines whether retrieval via this indexer is done via case-sensitive /// comparisons. By default, retrieval is not case sensitive. This makes /// sense on Windows, in which filesystems are not case sensitive. /// /// /// /// Regardless of case-sensitivity, it is not always the case that /// this[value].FileName == value. In other words, the FileName /// property of the ZipEntry retrieved with this indexer, may or may /// not be equal to the index value. /// /// /// /// This is because DotNetZip performs a normalization of filenames passed to /// this indexer, before attempting to retrieve the item. That normalization /// includes: removal of a volume letter and colon, swapping backward slashes /// for forward slashes. So, zip["dir1\\entry1.txt"].FileName == /// "dir1/entry.txt". /// /// /// /// Directory entries in the zip file may be retrieved via this indexer only /// with names that have a trailing slash. DotNetZip automatically appends a /// trailing slash to the names of any directory entries added to a zip. /// /// /// /// /// /// This example extracts only the entries in a zip file that are .txt files. /// /// using (ZipFile zip = ZipFile.Read("PackedDocuments.zip")) /// { /// foreach (string s1 in zip.EntryFilenames) /// { /// if (s1.EndsWith(".txt")) /// zip[s1].Extract("textfiles"); /// } /// } /// /// /// Using zip As ZipFile = ZipFile.Read("PackedDocuments.zip") /// Dim s1 As String /// For Each s1 In zip.EntryFilenames /// If s1.EndsWith(".txt") Then /// zip(s1).Extract("textfiles") /// End If /// Next /// End Using /// /// /// /// /// /// Thrown if the caller attempts to assign a non-null value to the indexer. /// /// /// /// The name of the file, including any directory path, to retrieve from the /// zip. The filename match is not case-sensitive by default; you can use the /// property to change this behavior. The /// pathname can use forward-slashes or backward slashes. /// /// /// /// The ZipEntry within the Zip archive, given by the specified /// filename. If the named entry does not exist in the archive, this indexer /// returns null (Nothing in VB). /// /// public ZipEntry this[String fileName] { get { var key = SharedUtilities.NormalizePathForUseInZipFile(fileName); if (_entries.ContainsKey(key)) return _entries[key]; // workitem 11056 key = key.Replace("/", "\\"); if (_entries.ContainsKey(key)) return _entries[key]; return null; #if MESSY foreach (ZipEntry e in _entries.Values) { if (this.CaseSensitiveRetrieval) { // check for the file match with a case-sensitive comparison. if (e.FileName == fileName) return e; // also check for equivalence if (fileName.Replace("\\", "/") == e.FileName) return e; if (e.FileName.Replace("\\", "/") == fileName) return e; // check for a difference only in trailing slash if (e.FileName.EndsWith("/")) { var fileNameNoSlash = e.FileName.Trim("/".ToCharArray()); if (fileNameNoSlash == fileName) return e; // also check for equivalence if (fileName.Replace("\\", "/") == fileNameNoSlash) return e; if (fileNameNoSlash.Replace("\\", "/") == fileName) return e; } } else { // check for the file match in a case-insensitive manner. if (String.Compare(e.FileName, fileName, StringComparison.CurrentCultureIgnoreCase) == 0) return e; // also check for equivalence if (String.Compare(fileName.Replace("\\", "/"), e.FileName, StringComparison.CurrentCultureIgnoreCase) == 0) return e; if (String.Compare(e.FileName.Replace("\\", "/"), fileName, StringComparison.CurrentCultureIgnoreCase) == 0) return e; // check for a difference only in trailing slash if (e.FileName.EndsWith("/")) { var fileNameNoSlash = e.FileName.Trim("/".ToCharArray()); if (String.Compare(fileNameNoSlash, fileName, StringComparison.CurrentCultureIgnoreCase) == 0) return e; // also check for equivalence if (String.Compare(fileName.Replace("\\", "/"), fileNameNoSlash, StringComparison.CurrentCultureIgnoreCase) == 0) return e; if (String.Compare(fileNameNoSlash.Replace("\\", "/"), fileName, StringComparison.CurrentCultureIgnoreCase) == 0) return e; } } } return null; #endif } } /// /// The list of filenames for the entries contained within the zip archive. /// /// /// /// According to the ZIP specification, the names of the entries use forward /// slashes in pathnames. If you are scanning through the list, you may have /// to swap forward slashes for backslashes. /// /// /// /// /// /// This example shows one way to test if a filename is already contained /// within a zip archive. /// /// String zipFileToRead= "PackedDocuments.zip"; /// string candidate = "DatedMaterial.xps"; /// using (ZipFile zip = new ZipFile(zipFileToRead)) /// { /// if (zip.EntryFilenames.Contains(candidate)) /// Console.WriteLine("The file '{0}' exists in the zip archive '{1}'", /// candidate, /// zipFileName); /// else /// Console.WriteLine("The file, '{0}', does not exist in the zip archive '{1}'", /// candidate, /// zipFileName); /// Console.WriteLine(); /// } /// /// /// Dim zipFileToRead As String = "PackedDocuments.zip" /// Dim candidate As String = "DatedMaterial.xps" /// Using zip As ZipFile.Read(ZipFileToRead) /// If zip.EntryFilenames.Contains(candidate) Then /// Console.WriteLine("The file '{0}' exists in the zip archive '{1}'", _ /// candidate, _ /// zipFileName) /// Else /// Console.WriteLine("The file, '{0}', does not exist in the zip archive '{1}'", _ /// candidate, _ /// zipFileName) /// End If /// Console.WriteLine /// End Using /// /// /// /// /// The list of strings for the filenames contained within the Zip archive. /// /// public System.Collections.Generic.ICollection EntryFileNames { get { return _entries.Keys; } } /// /// Returns the readonly collection of entries in the Zip archive. /// /// /// /// /// /// If there are no entries in the current ZipFile, the value returned is a /// non-null zero-element collection. If there are entries in the zip file, /// the elements are returned in no particular order. /// /// /// This is the implied enumerator on the ZipFile class. If you use a /// ZipFile instance in a context that expects an enumerator, you will /// get this collection. /// /// /// public System.Collections.Generic.ICollection Entries { get { return _entries.Values; } } /// /// Returns a readonly collection of entries in the Zip archive, sorted by FileName. /// /// /// /// If there are no entries in the current ZipFile, the value returned /// is a non-null zero-element collection. If there are entries in the zip /// file, the elements are returned sorted by the name of the entry. /// /// /// /// /// This example fills a Windows Forms ListView with the entries in a zip file. /// /// /// using (ZipFile zip = ZipFile.Read(zipFile)) /// { /// foreach (ZipEntry entry in zip.EntriesSorted) /// { /// ListViewItem item = new ListViewItem(n.ToString()); /// n++; /// string[] subitems = new string[] { /// entry.FileName.Replace("/","\\"), /// entry.LastModified.ToString("yyyy-MM-dd HH:mm:ss"), /// entry.UncompressedSize.ToString(), /// String.Format("{0,5:F0}%", entry.CompressionRatio), /// entry.CompressedSize.ToString(), /// (entry.UsesEncryption) ? "Y" : "N", /// String.Format("{0:X8}", entry.Crc)}; /// /// foreach (String s in subitems) /// { /// ListViewItem.ListViewSubItem subitem = new ListViewItem.ListViewSubItem(); /// subitem.Text = s; /// item.SubItems.Add(subitem); /// } /// /// this.listView1.Items.Add(item); /// } /// } /// /// /// /// public System.Collections.Generic.ICollection EntriesSorted { get { var coll = new System.Collections.Generic.List(); foreach (var e in this.Entries) { coll.Add(e); } StringComparison sc = (CaseSensitiveRetrieval) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; coll.Sort((x, y) => { return String.Compare(x.FileName, y.FileName, sc); }); return coll.AsReadOnly(); } } /// /// Returns the number of entries in the Zip archive. /// public int Count { get { return _entries.Count; } } /// /// Removes the given ZipEntry from the zip archive. /// /// /// /// /// After calling RemoveEntry, the application must call Save to /// make the changes permanent. /// /// /// /// /// Thrown if the specified ZipEntry does not exist in the ZipFile. /// /// /// /// In this example, all entries in the zip archive dating from before /// December 31st, 2007, are removed from the archive. This is actually much /// easier if you use the RemoveSelectedEntries method. But I needed an /// example for RemoveEntry, so here it is. /// /// String ZipFileToRead = "ArchiveToModify.zip"; /// System.DateTime Threshold = new System.DateTime(2007,12,31); /// using (ZipFile zip = ZipFile.Read(ZipFileToRead)) /// { /// var EntriesToRemove = new System.Collections.Generic.List<ZipEntry>(); /// foreach (ZipEntry e in zip) /// { /// if (e.LastModified < Threshold) /// { /// // We cannot remove the entry from the list, within the context of /// // an enumeration of said list. /// // So we add the doomed entry to a list to be removed later. /// EntriesToRemove.Add(e); /// } /// } /// /// // actually remove the doomed entries. /// foreach (ZipEntry zombie in EntriesToRemove) /// zip.RemoveEntry(zombie); /// /// zip.Comment= String.Format("This zip archive was updated at {0}.", /// System.DateTime.Now.ToString("G")); /// /// // save with a different name /// zip.Save("Archive-Updated.zip"); /// } /// /// /// /// Dim ZipFileToRead As String = "ArchiveToModify.zip" /// Dim Threshold As New DateTime(2007, 12, 31) /// Using zip As ZipFile = ZipFile.Read(ZipFileToRead) /// Dim EntriesToRemove As New System.Collections.Generic.List(Of ZipEntry) /// Dim e As ZipEntry /// For Each e In zip /// If (e.LastModified < Threshold) Then /// ' We cannot remove the entry from the list, within the context of /// ' an enumeration of said list. /// ' So we add the doomed entry to a list to be removed later. /// EntriesToRemove.Add(e) /// End If /// Next /// /// ' actually remove the doomed entries. /// Dim zombie As ZipEntry /// For Each zombie In EntriesToRemove /// zip.RemoveEntry(zombie) /// Next /// zip.Comment = String.Format("This zip archive was updated at {0}.", DateTime.Now.ToString("G")) /// 'save as a different name /// zip.Save("Archive-Updated.zip") /// End Using /// /// /// /// /// The ZipEntry to remove from the zip. /// /// /// /// public void RemoveEntry(ZipEntry entry) { //if (!_entries.Values.Contains(entry)) // throw new ArgumentException("The entry you specified does not exist in the zip archive."); if (entry == null) throw new ArgumentNullException("entry"); _entries.Remove(SharedUtilities.NormalizePathForUseInZipFile(entry.FileName)); _zipEntriesAsList = null; #if NOTNEEDED if (_direntries != null) { bool FoundAndRemovedDirEntry = false; foreach (ZipDirEntry de1 in _direntries) { if (entry.FileName == de1.FileName) { _direntries.Remove(de1); FoundAndRemovedDirEntry = true; break; } } if (!FoundAndRemovedDirEntry) throw new BadStateException("The entry to be removed was not found in the directory."); } #endif _contentsChanged = true; } /// /// Removes the ZipEntry with the given filename from the zip archive. /// /// /// /// /// After calling RemoveEntry, the application must call Save to /// make the changes permanent. /// /// /// /// /// /// Thrown if the ZipFile is not updatable. /// /// /// /// Thrown if a ZipEntry with the specified filename does not exist in /// the ZipFile. /// /// /// /// /// This example shows one way to remove an entry with a given filename from /// an existing zip archive. /// /// /// String zipFileToRead= "PackedDocuments.zip"; /// string candidate = "DatedMaterial.xps"; /// using (ZipFile zip = ZipFile.Read(zipFileToRead)) /// { /// if (zip.EntryFilenames.Contains(candidate)) /// { /// zip.RemoveEntry(candidate); /// zip.Comment= String.Format("The file '{0}' has been removed from this archive.", /// Candidate); /// zip.Save(); /// } /// } /// /// /// Dim zipFileToRead As String = "PackedDocuments.zip" /// Dim candidate As String = "DatedMaterial.xps" /// Using zip As ZipFile = ZipFile.Read(zipFileToRead) /// If zip.EntryFilenames.Contains(candidate) Then /// zip.RemoveEntry(candidate) /// zip.Comment = String.Format("The file '{0}' has been removed from this archive.", Candidate) /// zip.Save /// End If /// End Using /// /// /// /// /// The name of the file, including any directory path, to remove from the zip. /// The filename match is not case-sensitive by default; you can use the /// CaseSensitiveRetrieval property to change this behavior. The /// pathname can use forward-slashes or backward slashes. /// /// public void RemoveEntry(String fileName) { string modifiedName = ZipEntry.NameInArchive(fileName, null); ZipEntry e = this[modifiedName]; if (e == null) throw new ArgumentException("The entry you specified was not found in the zip archive."); RemoveEntry(e); } #endregion #region Destructors and Disposers // /// // /// This is the class Destructor, which gets called implicitly when the instance // /// is destroyed. Because the ZipFile type implements IDisposable, this // /// method calls Dispose(false). // /// // ~ZipFile() // { // // call Dispose with false. Since we're in the // // destructor call, the managed resources will be // // disposed of anyways. // Dispose(false); // } /// /// Closes the read and write streams associated /// to the ZipFile, if necessary. /// /// /// /// The Dispose() method is generally employed implicitly, via a using(..) {..} /// statement. (Using...End Using in VB) If you do not employ a using /// statement, insure that your application calls Dispose() explicitly. For /// example, in a Powershell application, or an application that uses the COM /// interop interface, you must call Dispose() explicitly. /// /// /// /// This example extracts an entry selected by name, from the Zip file to the /// Console. /// /// using (ZipFile zip = ZipFile.Read(zipfile)) /// { /// foreach (ZipEntry e in zip) /// { /// if (WantThisEntry(e.FileName)) /// zip.Extract(e.FileName, Console.OpenStandardOutput()); /// } /// } // Dispose() is called implicitly here. /// /// /// /// Using zip As ZipFile = ZipFile.Read(zipfile) /// Dim e As ZipEntry /// For Each e In zip /// If WantThisEntry(e.FileName) Then /// zip.Extract(e.FileName, Console.OpenStandardOutput()) /// End If /// Next /// End Using ' Dispose is implicity called here /// /// public void Dispose() { // dispose of the managed and unmanaged resources Dispose(true); // tell the GC that the Finalize process no longer needs // to be run for this object. GC.SuppressFinalize(this); } /// /// Disposes any managed resources, if the flag is set, then marks the /// instance disposed. This method is typically not called explicitly from /// application code. /// /// /// /// Applications should call the no-arg Dispose method. /// /// /// /// indicates whether the method should dispose streams or not. /// protected virtual void Dispose(bool disposeManagedResources) { if (!this._disposed) { if (disposeManagedResources) { // dispose managed resources if (_ReadStreamIsOurs) { if (_readstream != null) { // workitem 7704 #if NETCF _readstream.Close(); #else _readstream.Dispose(); #endif _readstream = null; } } // only dispose the writestream if there is a backing file if ((_temporaryFileName != null) && (_name != null)) if (_writestream != null) { // workitem 7704 #if NETCF _writestream.Close(); #else _writestream.Dispose(); #endif _writestream = null; } #if !NETCF // workitem 10030 if (this.ParallelDeflater != null) { this.ParallelDeflater.Dispose(); this.ParallelDeflater = null; } #endif } this._disposed = true; } } #endregion #region private properties internal Stream ReadStream { get { if (_readstream == null) { if (_readName != null || _name !=null) { _readstream = File.Open(_readName ?? _name, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Write); _ReadStreamIsOurs = true; } } return _readstream; } } private Stream WriteStream { // workitem 9763 get { if (_writestream != null) return _writestream; if (_name == null) return _writestream; if (_maxOutputSegmentSize != 0) { _writestream = ZipSegmentedStream.ForWriting(this._name, _maxOutputSegmentSize); return _writestream; } SharedUtilities.CreateAndOpenUniqueTempFile(TempFileFolder ?? Path.GetDirectoryName(_name), out _writestream, out _temporaryFileName); return _writestream; } set { if (value != null) throw new ZipException("Cannot set the stream to a non-null value."); _writestream = null; } } #endregion #region private fields private TextWriter _StatusMessageTextWriter; private bool _CaseSensitiveRetrieval; private Stream _readstream; private Stream _writestream; private UInt16 _versionMadeBy; private UInt16 _versionNeededToExtract; private UInt32 _diskNumberWithCd; private Int32 _maxOutputSegmentSize; private UInt32 _numberOfSegmentsForMostRecentSave; private ZipErrorAction _zipErrorAction; private bool _disposed; //private System.Collections.Generic.List _entries; private System.Collections.Generic.Dictionary _entries; private List _zipEntriesAsList; private string _name; private string _readName; private string _Comment; internal string _Password; private bool _emitNtfsTimes = true; private bool _emitUnixTimes; private Ionic.Zlib.CompressionStrategy _Strategy = Ionic.Zlib.CompressionStrategy.Default; private Ionic.Zip.CompressionMethod _compressionMethod = Ionic.Zip.CompressionMethod.Deflate; private bool _fileAlreadyExists; private string _temporaryFileName; private bool _contentsChanged; private bool _hasBeenSaved; private String _TempFileFolder; private bool _ReadStreamIsOurs = true; private object LOCK = new object(); private bool _saveOperationCanceled; private bool _extractOperationCanceled; private bool _addOperationCanceled; private EncryptionAlgorithm _Encryption; private bool _JustSaved; private long _locEndOfCDS = -1; private uint _OffsetOfCentralDirectory; private Int64 _OffsetOfCentralDirectory64; private Nullable _OutputUsesZip64; internal bool _inExtractAll; private System.Text.Encoding _alternateEncoding = System.Text.Encoding.GetEncoding("IBM437"); // UTF-8 private ZipOption _alternateEncodingUsage = ZipOption.Never; private static System.Text.Encoding _defaultEncoding = System.Text.Encoding.GetEncoding("IBM437"); private int _BufferSize = BufferSizeDefault; #if !NETCF internal Ionic.Zlib.ParallelDeflateOutputStream ParallelDeflater; private long _ParallelDeflateThreshold; private int _maxBufferPairs = 16; #endif internal Zip64Option _zip64 = Zip64Option.Default; #pragma warning disable 649 private bool _SavingSfx; #pragma warning restore 649 /// /// Default size of the buffer used for IO. /// public static readonly int BufferSizeDefault = 32768; #endregion } /// /// Options for using ZIP64 extensions when saving zip archives. /// /// /// /// /// /// Designed many years ago, the original zip /// specification from PKWARE allowed for 32-bit quantities for the /// compressed and uncompressed sizes of zip entries, as well as a 32-bit quantity /// for specifying the length of the zip archive itself, and a maximum of 65535 /// entries. These limits are now regularly exceeded in many backup and archival /// scenarios. Recently, PKWare added extensions to the original zip spec, called /// "ZIP64 extensions", to raise those limitations. This property governs whether /// DotNetZip will use those extensions when writing zip archives. The use of /// these extensions is optional and explicit in DotNetZip because, despite the /// status of ZIP64 as a bona fide standard, many other zip tools and libraries do /// not support ZIP64, and therefore a zip file with ZIP64 extensions may be /// unreadable by some of those other tools. /// /// /// /// Set this property to to always use ZIP64 /// extensions when saving, regardless of whether your zip archive needs it. /// Suppose you add 5 files, each under 100k, to a ZipFile. If you specify Always /// for this flag, you will get a ZIP64 archive, though the archive does not need /// to use ZIP64 because none of the original zip limits had been exceeded. /// /// /// /// Set this property to to tell the DotNetZip /// library to never use ZIP64 extensions. This is useful for maximum /// compatibility and interoperability, at the expense of the capability of /// handling large files or large archives. NB: Windows Explorer in Windows XP /// and Windows Vista cannot currently extract files from a zip64 archive, so if /// you want to guarantee that a zip archive produced by this library will work in /// Windows Explorer, use Never. If you set this property to , and your application creates a zip that would /// exceed one of the Zip limits, the library will throw an exception while saving /// the zip file. /// /// /// /// Set this property to to tell the /// DotNetZip library to use the ZIP64 extensions when required by the /// entry. After the file is compressed, the original and compressed sizes are /// checked, and if they exceed the limits described above, then zip64 can be /// used. That is the general idea, but there is an additional wrinkle when saving /// to a non-seekable device, like the ASP.NET Response.OutputStream, or /// Console.Out. When using non-seekable streams for output, the entry /// header - which indicates whether zip64 is in use - is emitted before it is /// known if zip64 is necessary. It is only after all entries have been saved /// that it can be known if ZIP64 will be required. On seekable output streams, /// after saving all entries, the library can seek backward and re-emit the zip /// file header to be consistent with the actual ZIP64 requirement. But using a /// non-seekable output stream, the library cannot seek backward, so the header /// can never be changed. In other words, the archive's use of ZIP64 extensions is /// not alterable after the header is emitted. Therefore, when saving to /// non-seekable streams, using is the same /// as using : it will always produce a zip /// archive that uses ZIP64 extensions. /// /// /// public enum Zip64Option { /// /// The default behavior, which is "Never". /// (For COM clients, this is a 0 (zero).) /// Default = 0, /// /// Do not use ZIP64 extensions when writing zip archives. /// (For COM clients, this is a 0 (zero).) /// Never = 0, /// /// Use ZIP64 extensions when writing zip archives, as necessary. /// For example, when a single entry exceeds 0xFFFFFFFF in size, or when the archive as a whole /// exceeds 0xFFFFFFFF in size, or when there are more than 65535 entries in an archive. /// (For COM clients, this is a 1.) /// AsNecessary = 1, /// /// Always use ZIP64 extensions when writing zip archives, even when unnecessary. /// (For COM clients, this is a 2.) /// Always } /// /// An enum representing the values on a three-way toggle switch /// for various options in the library. This might be used to /// specify whether to employ a particular text encoding, or to use /// ZIP64 extensions, or some other option. /// public enum ZipOption { /// /// The default behavior. This is the same as "Never". /// (For COM clients, this is a 0 (zero).) /// Default = 0, /// /// Never use the associated option. /// (For COM clients, this is a 0 (zero).) /// Never = 0, /// /// Use the associated behavior "as necessary." /// (For COM clients, this is a 1.) /// AsNecessary = 1, /// /// Use the associated behavior Always, whether necessary or not. /// (For COM clients, this is a 2.) /// Always } enum AddOrUpdateAction { AddOnly = 0, AddOrUpdate } } // ================================================================== // // Information on the ZIP format: // // From // http://www.pkware.com/documents/casestudies/APPNOTE.TXT // // Overall .ZIP file format: // // [local file header 1] // [file data 1] // [data descriptor 1] ** sometimes // . // . // . // [local file header n] // [file data n] // [data descriptor n] ** sometimes // [archive decryption header] // [archive extra data record] // [central directory] // [zip64 end of central directory record] // [zip64 end of central directory locator] // [end of central directory record] // // Local File Header format: // local file header signature ... 4 bytes (0x04034b50) // version needed to extract ..... 2 bytes // general purpose bit field ..... 2 bytes // compression method ............ 2 bytes // last mod file time ............ 2 bytes // last mod file date............. 2 bytes // crc-32 ........................ 4 bytes // compressed size................ 4 bytes // uncompressed size.............. 4 bytes // file name length............... 2 bytes // extra field length ............ 2 bytes // file name varies // extra field varies // // // Data descriptor: (used only when bit 3 of the general purpose bitfield is set) // (although, I have found zip files where bit 3 is not set, yet this descriptor is present!) // local file header signature 4 bytes (0x08074b50) ** sometimes!!! Not always // crc-32 4 bytes // compressed size 4 bytes // uncompressed size 4 bytes // // // Central directory structure: // // [file header 1] // . // . // . // [file header n] // [digital signature] // // // File header: (This is a ZipDirEntry) // central file header signature 4 bytes (0x02014b50) // version made by 2 bytes // version needed to extract 2 bytes // general purpose bit flag 2 bytes // compression method 2 bytes // last mod file time 2 bytes // last mod file date 2 bytes // crc-32 4 bytes // compressed size 4 bytes // uncompressed size 4 bytes // file name length 2 bytes // extra field length 2 bytes // file comment length 2 bytes // disk number start 2 bytes // internal file attributes ** 2 bytes // external file attributes *** 4 bytes // relative offset of local header 4 bytes // file name (variable size) // extra field (variable size) // file comment (variable size) // // ** The internal file attributes, near as I can tell, // uses 0x01 for a file and a 0x00 for a directory. // // ***The external file attributes follows the MS-DOS file attribute byte, described here: // at http://support.microsoft.com/kb/q125019/ // 0x0010 => directory // 0x0020 => file // // // End of central directory record: // // end of central dir signature 4 bytes (0x06054b50) // number of this disk 2 bytes // number of the disk with the // start of the central directory 2 bytes // total number of entries in the // central directory on this disk 2 bytes // total number of entries in // the central directory 2 bytes // size of the central directory 4 bytes // offset of start of central // directory with respect to // the starting disk number 4 bytes // .ZIP file comment length 2 bytes // .ZIP file comment (variable size) // // date and time are packed values, as MSDOS did them // time: bits 0-4 : seconds (divided by 2) // 5-10: minute // 11-15: hour // date bits 0-4 : day // 5-8: month // 9-15 year (since 1980) // // see http://msdn.microsoft.com/en-us/library/ms724274(VS.85).aspx