| // ZipOutputStream.cs |
| // |
| // ------------------------------------------------------------------ |
| // |
| // Copyright (c) 2009 Dino Chiesa. |
| // All rights reserved. |
| // |
| // This code module is part of DotNetZip, a zipfile class library. |
| // |
| // ------------------------------------------------------------------ |
| // |
| // This code is licensed under the Microsoft Public License. |
| // See the file License.txt for the license details. |
| // More info on: http://dotnetzip.codeplex.com |
| // |
| // ------------------------------------------------------------------ |
| // |
| // last saved (in emacs): |
| // Time-stamp: <2011-July-28 06:34:30> |
| // |
| // ------------------------------------------------------------------ |
| // |
| // This module defines the ZipOutputStream class, which is a stream metaphor for |
| // generating zip files. This class does not depend on Ionic.Zip.ZipFile, but rather |
| // stands alongside it as an alternative "container" for ZipEntry. It replicates a |
| // subset of the properties, including these: |
| // |
| // - Comment |
| // - Encryption |
| // - Password |
| // - CodecBufferSize |
| // - CompressionLevel |
| // - CompressionMethod |
| // - EnableZip64 (UseZip64WhenSaving) |
| // - IgnoreCase (!CaseSensitiveRetrieval) |
| // |
| // It adds these novel methods: |
| // |
| // - PutNextEntry |
| // |
| // |
| // ------------------------------------------------------------------ |
| // |
| |
| using System; |
| using System.Threading; |
| using System.Collections.Generic; |
| using System.IO; |
| using Ionic.Zip; |
| using OfficeOpenXml.Packaging.Ionic.Zlib; |
| |
| namespace OfficeOpenXml.Packaging.Ionic.Zip |
| { |
| /// <summary> |
| /// Provides a stream metaphor for generating zip files. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// This class writes zip files, as defined in the <see |
| /// href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">specification |
| /// for zip files described by PKWare</see>. 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. |
| /// </para> |
| /// |
| /// <para> |
| /// This class provides an alternative programming model to the one enabled by the |
| /// <see cref="ZipFile"/> class. Use this when creating zip files, as an |
| /// alternative to the <see cref="ZipFile"/> class, when you would like to use a |
| /// <c>Stream</c> type to write the zip file. |
| /// </para> |
| /// |
| /// <para> |
| /// Both the <c>ZipOutputStream</c> class and the <c>ZipFile</c> class can be used |
| /// to create zip files. Both of them support many of the common zip features, |
| /// including Unicode, different compression levels, and ZIP64. They provide |
| /// very similar performance when creating zip files. |
| /// </para> |
| /// |
| /// <para> |
| /// The <c>ZipFile</c> class is generally easier to use than |
| /// <c>ZipOutputStream</c> and should be considered a higher-level interface. For |
| /// example, when creating a zip file via calls to the <c>PutNextEntry()</c> and |
| /// <c>Write()</c> methods on the <c>ZipOutputStream</c> class, the caller is |
| /// responsible for opening the file, reading the bytes from the file, writing |
| /// those bytes into the <c>ZipOutputStream</c>, setting the attributes on the |
| /// <c>ZipEntry</c>, 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 <see cref="ZipFile.AddFile(string,string)">ZipFile.AddFile()</see>. |
| /// For this reason, the <c>ZipOutputStream</c> 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 <c>Stream</c> |
| /// metaphor. |
| /// </para> |
| /// |
| /// <para> |
| /// Aside from the differences in programming model, there are other |
| /// differences in capability between the two classes. |
| /// </para> |
| /// |
| /// <list type="bullet"> |
| /// <item> |
| /// <c>ZipFile</c> can be used to read and extract zip files, in addition to |
| /// creating zip files. <c>ZipOutputStream</c> cannot read zip files. If you want |
| /// to use a stream to read zip files, check out the <see cref="ZipInputStream"/> class. |
| /// </item> |
| /// |
| /// <item> |
| /// <c>ZipOutputStream</c> does not support the creation of segmented or spanned |
| /// zip files. |
| /// </item> |
| /// |
| /// <item> |
| /// <c>ZipOutputStream</c> cannot produce a self-extracting archive. |
| /// </item> |
| /// </list> |
| /// |
| /// <para> |
| /// Be aware that the <c>ZipOutputStream</c> class implements the <see |
| /// cref="System.IDisposable"/> interface. In order for |
| /// <c>ZipOutputStream</c> to produce a valid zip file, you use use it within |
| /// a using clause (<c>Using</c> in VB), or call the <c>Dispose()</c> method |
| /// explicitly. See the examples for how to employ a using clause. |
| /// </para> |
| /// |
| /// <para> |
| /// Also, a note regarding compression performance: On the desktop .NET |
| /// Framework, DotNetZip can use a multi-threaded compression implementation |
| /// that provides significant speed increases on large files, over 300k or so, |
| /// at the cost of increased memory use at runtime. (The output of the |
| /// compression is almost exactly the same size). But, the multi-threaded |
| /// approach incurs a performance hit on smaller files. There's no way for the |
| /// ZipOutputStream to know whether parallel compression will be beneficial, |
| /// because the ZipOutputStream does not know how much data you will write |
| /// through the stream. You may wish to set the <see |
| /// cref="ParallelDeflateThreshold"/> property to zero, if you are compressing |
| /// large files through <c>ZipOutputStream</c>. This will cause parallel |
| /// compression to be used, always. |
| /// </para> |
| /// </remarks> |
| internal class ZipOutputStream : Stream |
| { |
| /// <summary> |
| /// Create a ZipOutputStream, wrapping an existing stream. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// The <see cref="ZipFile"/> class is generally easier to use when creating |
| /// zip files. The ZipOutputStream offers a different metaphor for creating a |
| /// zip file, based on the <see cref="System.IO.Stream"/> class. |
| /// </para> |
| /// |
| /// </remarks> |
| /// |
| /// <param name="stream"> |
| /// The stream to wrap. It must be writable. This stream will be closed at |
| /// the time the ZipOutputStream is closed. |
| /// </param> |
| /// |
| /// <example> |
| /// |
| /// This example shows how to create a zip file, using the |
| /// ZipOutputStream class. |
| /// |
| /// <code lang="C#"> |
| /// private void Zipup() |
| /// { |
| /// if (filesToZip.Count == 0) |
| /// { |
| /// System.Console.WriteLine("Nothing to do."); |
| /// return; |
| /// } |
| /// |
| /// using (var raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite )) |
| /// { |
| /// using (var output= new ZipOutputStream(raw)) |
| /// { |
| /// output.Password = "VerySecret!"; |
| /// output.Encryption = EncryptionAlgorithm.WinZipAes256; |
| /// |
| /// foreach (string inputFileName in filesToZip) |
| /// { |
| /// System.Console.WriteLine("file: {0}", inputFileName); |
| /// |
| /// output.PutNextEntry(inputFileName); |
| /// using (var input = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Write )) |
| /// { |
| /// byte[] buffer= new byte[2048]; |
| /// int n; |
| /// while ((n= input.Read(buffer,0,buffer.Length)) > 0) |
| /// { |
| /// output.Write(buffer,0,n); |
| /// } |
| /// } |
| /// } |
| /// } |
| /// } |
| /// } |
| /// </code> |
| /// |
| /// <code lang="VB"> |
| /// Private Sub Zipup() |
| /// Dim outputFileName As String = "XmlData.zip" |
| /// Dim filesToZip As String() = Directory.GetFiles(".", "*.xml") |
| /// If (filesToZip.Length = 0) Then |
| /// Console.WriteLine("Nothing to do.") |
| /// Else |
| /// Using raw As FileStream = File.Open(outputFileName, FileMode.Create, FileAccess.ReadWrite) |
| /// Using output As ZipOutputStream = New ZipOutputStream(raw) |
| /// output.Password = "VerySecret!" |
| /// output.Encryption = EncryptionAlgorithm.WinZipAes256 |
| /// Dim inputFileName As String |
| /// For Each inputFileName In filesToZip |
| /// Console.WriteLine("file: {0}", inputFileName) |
| /// output.PutNextEntry(inputFileName) |
| /// Using input As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) |
| /// Dim n As Integer |
| /// Dim buffer As Byte() = New Byte(2048) {} |
| /// Do While (n = input.Read(buffer, 0, buffer.Length) > 0) |
| /// output.Write(buffer, 0, n) |
| /// Loop |
| /// End Using |
| /// Next |
| /// End Using |
| /// End Using |
| /// End If |
| /// End Sub |
| /// </code> |
| /// </example> |
| public ZipOutputStream(Stream stream) : this(stream, false) { } |
| |
| |
| /// <summary> |
| /// Create a ZipOutputStream that writes to a filesystem file. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// The <see cref="ZipFile"/> class is generally easier to use when creating |
| /// zip files. The ZipOutputStream offers a different metaphor for creating a |
| /// zip file, based on the <see cref="System.IO.Stream"/> class. |
| /// </remarks> |
| /// |
| /// <param name="fileName"> |
| /// The name of the zip file to create. |
| /// </param> |
| /// |
| /// <example> |
| /// |
| /// This example shows how to create a zip file, using the |
| /// ZipOutputStream class. |
| /// |
| /// <code lang="C#"> |
| /// private void Zipup() |
| /// { |
| /// if (filesToZip.Count == 0) |
| /// { |
| /// System.Console.WriteLine("Nothing to do."); |
| /// return; |
| /// } |
| /// |
| /// using (var output= new ZipOutputStream(outputFileName)) |
| /// { |
| /// output.Password = "VerySecret!"; |
| /// output.Encryption = EncryptionAlgorithm.WinZipAes256; |
| /// |
| /// foreach (string inputFileName in filesToZip) |
| /// { |
| /// System.Console.WriteLine("file: {0}", inputFileName); |
| /// |
| /// output.PutNextEntry(inputFileName); |
| /// using (var input = File.Open(inputFileName, FileMode.Open, FileAccess.Read, |
| /// FileShare.Read | FileShare.Write )) |
| /// { |
| /// byte[] buffer= new byte[2048]; |
| /// int n; |
| /// while ((n= input.Read(buffer,0,buffer.Length)) > 0) |
| /// { |
| /// output.Write(buffer,0,n); |
| /// } |
| /// } |
| /// } |
| /// } |
| /// } |
| /// </code> |
| /// |
| /// <code lang="VB"> |
| /// Private Sub Zipup() |
| /// Dim outputFileName As String = "XmlData.zip" |
| /// Dim filesToZip As String() = Directory.GetFiles(".", "*.xml") |
| /// If (filesToZip.Length = 0) Then |
| /// Console.WriteLine("Nothing to do.") |
| /// Else |
| /// Using output As ZipOutputStream = New ZipOutputStream(outputFileName) |
| /// output.Password = "VerySecret!" |
| /// output.Encryption = EncryptionAlgorithm.WinZipAes256 |
| /// Dim inputFileName As String |
| /// For Each inputFileName In filesToZip |
| /// Console.WriteLine("file: {0}", inputFileName) |
| /// output.PutNextEntry(inputFileName) |
| /// Using input As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) |
| /// Dim n As Integer |
| /// Dim buffer As Byte() = New Byte(2048) {} |
| /// Do While (n = input.Read(buffer, 0, buffer.Length) > 0) |
| /// output.Write(buffer, 0, n) |
| /// Loop |
| /// End Using |
| /// Next |
| /// End Using |
| /// End If |
| /// End Sub |
| /// </code> |
| /// </example> |
| public ZipOutputStream(String fileName) |
| { |
| Stream stream = File.Open(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.None); |
| _Init(stream, false, fileName); |
| } |
| |
| |
| /// <summary> |
| /// Create a ZipOutputStream. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// See the documentation for the <see |
| /// cref="ZipOutputStream(Stream)">ZipOutputStream(Stream)</see> |
| /// constructor for an example. |
| /// </remarks> |
| /// |
| /// <param name="stream"> |
| /// The stream to wrap. It must be writable. |
| /// </param> |
| /// |
| /// <param name="leaveOpen"> |
| /// true if the application would like the stream |
| /// to remain open after the <c>ZipOutputStream</c> has been closed. |
| /// </param> |
| public ZipOutputStream(Stream stream, bool leaveOpen) |
| { |
| _Init(stream, leaveOpen, null); |
| } |
| |
| private void _Init(Stream stream, bool leaveOpen, string name) |
| { |
| // workitem 9307 |
| _outputStream = stream.CanRead ? stream : new CountingStream(stream); |
| CompressionLevel = OfficeOpenXml.Packaging.Ionic.Zlib.CompressionLevel.Default; |
| CompressionMethod = OfficeOpenXml.Packaging.Ionic.Zip.CompressionMethod.Deflate; |
| _encryption = EncryptionAlgorithm.None; |
| _entriesWritten = new Dictionary<String, ZipEntry>(StringComparer.Ordinal); |
| _zip64 = Zip64Option.Never; |
| _leaveUnderlyingStreamOpen = leaveOpen; |
| Strategy = Ionic.Zlib.CompressionStrategy.Default; |
| _name = name ?? "(stream)"; |
| #if !NETCF |
| ParallelDeflateThreshold = -1L; |
| #endif |
| } |
| |
| |
| /// <summary>Provides a string representation of the instance.</summary> |
| /// <remarks> |
| /// <para> |
| /// This can be useful for debugging purposes. |
| /// </para> |
| /// </remarks> |
| /// <returns>a string representation of the instance.</returns> |
| public override String ToString() |
| { |
| return String.Format ("ZipOutputStream::{0}(leaveOpen({1})))", _name, _leaveUnderlyingStreamOpen); |
| } |
| |
| |
| /// <summary> |
| /// Sets the password to be used on the <c>ZipOutputStream</c> instance. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// |
| /// <para> |
| /// When writing a zip archive, this password is applied to the entries, not |
| /// to the zip archive itself. It applies to any <c>ZipEntry</c> subsequently |
| /// written to the <c>ZipOutputStream</c>. |
| /// </para> |
| /// |
| /// <para> |
| /// Using a password does not encrypt or protect the "directory" of the |
| /// archive - the list of entries contained in the archive. If you set the |
| /// <c>Password</c> 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. |
| /// </para> |
| /// |
| /// <para> |
| /// If you set this property, and then add a set of entries to the archive via |
| /// calls to <c>PutNextEntry</c>, 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 <c>null</c> (<c>Nothing</c> in VB), and add another entry, the |
| /// first entry is encrypted and the second is not. |
| /// </para> |
| /// |
| /// <para> |
| /// When setting the <c>Password</c>, you may also want to explicitly set the <see |
| /// cref="Encryption"/> property, to specify how to encrypt the entries added |
| /// to the ZipFile. If you set the <c>Password</c> to a non-null value and do not |
| /// set <see cref="Encryption"/>, then PKZip 2.0 ("Weak") encryption is used. |
| /// This encryption is relatively weak but is very interoperable. If |
| /// you set the password to a <c>null</c> value (<c>Nothing</c> in VB), |
| /// <c>Encryption</c> is reset to None. |
| /// </para> |
| /// |
| /// <para> |
| /// Special case: if you wrap a ZipOutputStream around a non-seekable stream, |
| /// and use encryption, and emit an entry of zero bytes, the <c>Close()</c> or |
| /// <c>PutNextEntry()</c> following the entry will throw an exception. |
| /// </para> |
| /// |
| /// </remarks> |
| public String Password |
| { |
| set |
| { |
| if (_disposed) |
| { |
| _exceptionPending = true; |
| throw new System.InvalidOperationException("The stream has been closed."); |
| } |
| |
| _password = value; |
| if (_password == null) |
| { |
| _encryption = EncryptionAlgorithm.None; |
| } |
| else if (_encryption == EncryptionAlgorithm.None) |
| { |
| _encryption = EncryptionAlgorithm.PkzipWeak; |
| } |
| } |
| } |
| |
| |
| /// <summary> |
| /// The Encryption to use for entries added to the <c>ZipOutputStream</c>. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// The specified Encryption is applied to the entries subsequently |
| /// written to the <c>ZipOutputStream</c> instance. |
| /// </para> |
| /// |
| /// <para> |
| /// If you set this to something other than |
| /// EncryptionAlgorithm.None, you will also need to set the |
| /// <see cref="Password"/> to a non-null, non-empty value in |
| /// order to actually get encryption on the entry. |
| /// </para> |
| /// |
| /// </remarks> |
| /// |
| /// <seealso cref="Password">ZipOutputStream.Password</seealso> |
| /// <seealso cref="ZipEntry.Encryption">ZipEntry.Encryption</seealso> |
| public EncryptionAlgorithm Encryption |
| { |
| get |
| { |
| return _encryption; |
| } |
| set |
| { |
| if (_disposed) |
| { |
| _exceptionPending = true; |
| throw new System.InvalidOperationException("The stream has been closed."); |
| } |
| if (value == EncryptionAlgorithm.Unsupported) |
| { |
| _exceptionPending = true; |
| throw new InvalidOperationException("You may not set Encryption to that value."); |
| } |
| _encryption = value; |
| } |
| } |
| |
| |
| /// <summary> |
| /// Size of the work buffer to use for the ZLIB codec during compression. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// Setting this may affect performance. For larger files, setting this to a |
| /// larger size may improve performance, but I'm not sure. Sorry, I don't |
| /// currently have good recommendations on how to set it. You can test it if |
| /// you like. |
| /// </remarks> |
| public int CodecBufferSize |
| { |
| get; |
| set; |
| } |
| |
| |
| /// <summary> |
| /// The compression strategy to use for all entries. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// Set the Strategy used by the ZLIB-compatible compressor, when compressing |
| /// data for the entries in the zip archive. 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 <see |
| /// cref="Ionic.Zlib.CompressionStrategy "/>. |
| /// </remarks> |
| public CompressionStrategy Strategy |
| { |
| get; |
| set; |
| } |
| |
| |
| /// <summary> |
| /// The type of timestamp attached to the ZipEntry. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// Set this in order to specify the kind of timestamp that should be emitted |
| /// into the zip file for each entry. |
| /// </remarks> |
| public ZipEntryTimestamp Timestamp |
| { |
| get |
| { |
| return _timestamp; |
| } |
| set |
| { |
| if (_disposed) |
| { |
| _exceptionPending = true; |
| throw new System.InvalidOperationException("The stream has been closed."); |
| } |
| _timestamp = value; |
| } |
| } |
| |
| |
| /// <summary> |
| /// Sets the compression level to be used for entries subsequently added to |
| /// the zip archive. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// Varying the compression level used on entries can affect the |
| /// size-vs-speed tradeoff when compression and decompressing data streams |
| /// or files. |
| /// </para> |
| /// |
| /// <para> |
| /// As with some other properties on the <c>ZipOutputStream</c> class, like <see |
| /// cref="Password"/>, and <see cref="Encryption"/>, |
| /// setting this property on a <c>ZipOutputStream</c> |
| /// instance will cause the specified <c>CompressionLevel</c> to be used on all |
| /// <see cref="ZipEntry"/> items that are subsequently added to the |
| /// <c>ZipOutputStream</c> instance. |
| /// </para> |
| /// |
| /// <para> |
| /// 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 <c>BestCompression</c> 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. |
| /// </para> |
| /// </remarks> |
| public OfficeOpenXml.Packaging.Ionic.Zlib.CompressionLevel CompressionLevel |
| { |
| get; |
| set; |
| } |
| |
| /// <summary> |
| /// The compression method used on each entry added to the ZipOutputStream. |
| /// </summary> |
| public CompressionMethod CompressionMethod |
| { |
| get; |
| set; |
| } |
| |
| |
| /// <summary> |
| /// A comment attached to the zip archive. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// |
| /// <para> |
| /// The application sets this property to specify a comment to be embedded |
| /// into the generated zip archive. |
| /// </para> |
| /// |
| /// <para> |
| /// According to <see |
| /// href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">PKWARE's |
| /// zip specification</see>, the comment is not encrypted, even if there is a |
| /// password set on the zip file. |
| /// </para> |
| /// |
| /// <para> |
| /// 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 <see cref="AlternateEncodingUsage"/> is |
| /// <c>Never</c>, it uses the default encoding (IBM437). (b) if <see |
| /// cref="AlternateEncodingUsage"/> is <c>Always</c>, it always uses the |
| /// alternate encoding (<see cref="AlternateEncoding"/>). (c) if <see |
| /// cref="AlternateEncodingUsage"/> is <c>AsNecessary</c>, 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 <c>ZipFile.Save()</c>. |
| /// </para> |
| /// |
| /// </remarks> |
| public string Comment |
| { |
| get { return _comment; } |
| set |
| { |
| if (_disposed) |
| { |
| _exceptionPending = true; |
| throw new System.InvalidOperationException("The stream has been closed."); |
| } |
| _comment = value; |
| } |
| } |
| |
| |
| |
| /// <summary> |
| /// Specify whether to use ZIP64 extensions when saving a zip archive. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// The default value for the property is <see |
| /// cref="Zip64Option.Never"/>. <see cref="Zip64Option.AsNecessary"/> is |
| /// safest, in the sense that you will not get an Exception if a |
| /// pre-ZIP64 limit is exceeded. |
| /// </para> |
| /// |
| /// <para> |
| /// You must set this property before calling <c>Write()</c>. |
| /// </para> |
| /// |
| /// </remarks> |
| public Zip64Option EnableZip64 |
| { |
| get |
| { |
| return _zip64; |
| } |
| set |
| { |
| if (_disposed) |
| { |
| _exceptionPending = true; |
| throw new System.InvalidOperationException("The stream has been closed."); |
| } |
| _zip64 = value; |
| } |
| } |
| |
| |
| /// <summary> |
| /// Indicates whether ZIP64 extensions were used when saving the zip archive. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// The value is defined only after the <c>ZipOutputStream</c> has been closed. |
| /// </remarks> |
| public bool OutputUsedZip64 |
| { |
| get |
| { |
| return _anyEntriesUsedZip64 || _directoryNeededZip64; |
| } |
| } |
| |
| |
| /// <summary> |
| /// Whether the ZipOutputStream should use case-insensitive comparisons when |
| /// checking for uniqueness of zip entries. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// Though the zip specification doesn't prohibit zipfiles with duplicate |
| /// entries, Sane zip files have no duplicates, and the DotNetZip library |
| /// cannot create zip files with duplicate entries. If an application attempts |
| /// to call <see cref="PutNextEntry(String)"/> with a name that duplicates one |
| /// already used within the archive, the library will throw an Exception. |
| /// </para> |
| /// <para> |
| /// This property allows the application to specify whether the |
| /// ZipOutputStream instance considers ordinal case when checking for |
| /// uniqueness of zip entries. |
| /// </para> |
| /// </remarks> |
| public bool IgnoreCase |
| { |
| get |
| { |
| return !_DontIgnoreCase; |
| } |
| |
| set |
| { |
| _DontIgnoreCase = !value; |
| } |
| |
| } |
| |
| |
| /// <summary> |
| /// Indicates whether to encode entry filenames and entry comments using |
| /// Unicode (UTF-8). |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// <see href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">The |
| /// PKWare zip specification</see> 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. |
| /// </para> |
| /// |
| /// <para> |
| /// If you do not set this flag, it will remain false. If this flag is false, |
| /// the <c>ZipOutputStream</c> 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". |
| /// </para> |
| /// |
| /// <para> |
| /// 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 <c>ZipOutputStream</c>, does not set the |
| /// encoding, 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, the zipfile will save properly, but extracting that file |
| /// later, with any zip tool, will result in an error, 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. |
| /// </para> |
| /// |
| /// <para> |
| /// Rather than specify the encoding in a binary fashion using this flag, an |
| /// application can specify an arbitrary encoding via the <see |
| /// cref="ProvisionalAlternateEncoding"/> 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. |
| /// </para> |
| /// </remarks> |
| /// <seealso cref="ProvisionalAlternateEncoding"/> |
| [Obsolete("Beginning with v1.9.1.6 of DotNetZip, this property is obsolete. It will be removed in a future version of the library. Use AlternateEncoding and AlternateEncodingUsage instead.")] |
| public bool UseUnicodeAsNecessary |
| { |
| get |
| { |
| return (_alternateEncoding == System.Text.Encoding.UTF8) && |
| (AlternateEncodingUsage == ZipOption.AsNecessary); |
| } |
| set |
| { |
| if (value) |
| { |
| _alternateEncoding = System.Text.Encoding.UTF8; |
| _alternateEncodingUsage = ZipOption.AsNecessary; |
| |
| } |
| else |
| { |
| _alternateEncoding = Ionic.Zip.ZipOutputStream.DefaultEncoding; |
| _alternateEncodingUsage = ZipOption.Never; |
| } |
| } |
| } |
| |
| |
| /// <summary> |
| /// The text encoding to use when emitting entries into the zip archive, for |
| /// those entries whose filenames or comments cannot be encoded with the |
| /// default (IBM437) encoding. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// In <see href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">its |
| /// zip specification</see>, 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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 <see cref="ZipEntry.FileName"/> and <see |
| /// cref="ZipEntry.Comment"/> for each <c>ZipEntry</c> 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 |
| /// <c>Comment</c> encoded in IBM437 and a <c>FileName</c> encoded with the |
| /// specified "provisional" codepage. |
| /// </para> |
| /// |
| /// <para> |
| /// Be aware that a zip file created after you've explicitly set the |
| /// <c>ProvisionalAlternateEncoding</c> 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. |
| /// </para> |
| /// |
| /// <para> |
| /// When creating a zip archive using this library, it is possible to change |
| /// the value of <c>ProvisionalAlternateEncoding</c> between each entry you |
| /// add, and between adding entries and the call to <c>Close()</c>. Don't do |
| /// this. It will likely result in a zipfile that is not readable. For best |
| /// interoperability, either leave <c>ProvisionalAlternateEncoding</c> |
| /// alone, or specify it only once, before adding any entries to the |
| /// <c>ZipOutputStream</c> instance. There is one exception to this |
| /// recommendation, described later. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. 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. |
| /// </para> |
| /// |
| /// <para> |
| /// One possible approach 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 <c>ProvisionalAlternateEncoding</c> to your desired |
| /// region-specific code page, once before adding any entries, and then set |
| /// the <see cref="Comment"/> property and reset |
| /// <c>ProvisionalAlternateEncoding</c> to IBM437 before calling <c>Close()</c>. |
| /// </para> |
| /// </remarks> |
| [Obsolete("use AlternateEncoding and AlternateEncodingUsage instead.")] |
| public System.Text.Encoding ProvisionalAlternateEncoding |
| { |
| get |
| { |
| if (_alternateEncodingUsage == ZipOption.AsNecessary) |
| return _alternateEncoding; |
| return null; |
| } |
| set |
| { |
| _alternateEncoding = value; |
| _alternateEncodingUsage = ZipOption.AsNecessary; |
| } |
| } |
| |
| /// <summary> |
| /// A Text Encoding to use when encoding the filenames and comments for |
| /// all the ZipEntry items, during a ZipFile.Save() operation. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Whether the encoding specified here is used during the save depends |
| /// on <see cref="AlternateEncodingUsage"/>. |
| /// </para> |
| /// </remarks> |
| public System.Text.Encoding AlternateEncoding |
| { |
| get |
| { |
| return _alternateEncoding; |
| } |
| set |
| { |
| _alternateEncoding = value; |
| } |
| } |
| |
| /// <summary> |
| /// 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. |
| /// </summary> |
| public ZipOption AlternateEncodingUsage |
| { |
| get |
| { |
| return _alternateEncodingUsage; |
| } |
| set |
| { |
| _alternateEncodingUsage = value; |
| } |
| } |
| |
| /// <summary> |
| /// The default text encoding used in zip archives. It is numeric 437, also |
| /// known as IBM437. |
| /// </summary> |
| /// <seealso cref="Ionic.Zip.ZipFile.ProvisionalAlternateEncoding"/> |
| public static System.Text.Encoding DefaultEncoding |
| { |
| get |
| { |
| return System.Text.Encoding.GetEncoding("IBM437"); |
| } |
| } |
| |
| |
| #if !NETCF |
| /// <summary> |
| /// The size threshold for an entry, above which a parallel deflate is used. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// |
| /// <para> |
| /// DotNetZip will use multiple threads to compress any ZipEntry, when |
| /// the <c>CompressionMethod</c> is Deflate, and if the entry is |
| /// larger than the given size. Zero means "always use parallel |
| /// deflate", while -1 means "never use parallel deflate". |
| /// </para> |
| /// |
| /// <para> |
| /// If the entry size cannot be known before compression, as with any entry |
| /// added via a ZipOutputStream, then Parallel deflate will never be |
| /// performed, unless the value of this property is zero. |
| /// </para> |
| /// |
| /// <para> |
| /// 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 slightly less effective. |
| /// </para> |
| /// |
| /// <para> |
| /// Parallel deflate tends to not be as effective as single-threaded deflate |
| /// 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, and additional framing bytes must be added to the output |
| /// stream. For that reason, a deflated stream may be slightly larger when |
| /// compressed using parallel deflate, as compared to a traditional |
| /// single-threaded deflate. For files of about 512k, the increase over the |
| /// normal deflate is as much as 5% of the total compressed size. For larger |
| /// files, the difference can be as small as 0.1%. |
| /// </para> |
| /// |
| /// <para> |
| /// 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 <see |
| /// cref="Ionic.Zlib.CompressionLevel.BestSpeed"/>. You may have to perform |
| /// some tests to determine the best approach for your situation. |
| /// </para> |
| /// |
| /// <para> |
| /// The default value for this property is -1, which means parallel |
| /// compression will not be performed unless you set it to zero. |
| /// </para> |
| /// |
| /// </remarks> |
| public long ParallelDeflateThreshold |
| { |
| set |
| { |
| if ((value != 0) && (value != -1) && (value < 64 * 1024)) |
| throw new ArgumentOutOfRangeException("value must be greater than 64k, or 0, or -1"); |
| _ParallelDeflateThreshold = value; |
| } |
| get |
| { |
| return _ParallelDeflateThreshold; |
| } |
| } |
| |
| |
| /// <summary> |
| /// The maximum number of buffer pairs to use when performing |
| /// parallel compression. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// 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 <see |
| /// cref="System.Threading.ThreadPool.QueueUserWorkItem(WaitCallback)"> |
| /// ThreadPool.QueueUserWorkItem()</see> to compress those |
| /// buffers in a background threadpool thread. After a buffer |
| /// is compressed, it is re-ordered and written to the output |
| /// stream. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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 (<see |
| /// cref="CodecBufferSize"/>). 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. |
| /// </para> |
| /// |
| /// <para> |
| /// 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. |
| /// </para> |
| /// |
| /// <para> |
| /// The application can set this value at any time, but it is |
| /// effective only if set before calling |
| /// <c>ZipOutputStream.Write()</c> for the first time. |
| /// </para> |
| /// </remarks> |
| /// |
| /// <seealso cref="ParallelDeflateThreshold"/> |
| /// |
| public int ParallelDeflateMaxBufferPairs |
| { |
| get |
| { |
| return _maxBufferPairs; |
| } |
| set |
| { |
| if (value < 4) |
| throw new ArgumentOutOfRangeException("ParallelDeflateMaxBufferPairs", |
| "Value must be 4 or greater."); |
| _maxBufferPairs = value; |
| } |
| } |
| #endif |
| |
| |
| private void InsureUniqueEntry(ZipEntry ze1) |
| { |
| if (_entriesWritten.ContainsKey(ze1.FileName)) |
| { |
| _exceptionPending = true; |
| throw new ArgumentException(String.Format("The entry '{0}' already exists in the zip archive.", ze1.FileName)); |
| } |
| } |
| |
| |
| internal Stream OutputStream |
| { |
| get |
| { |
| return _outputStream; |
| } |
| } |
| |
| internal String Name |
| { |
| get |
| { |
| return _name; |
| } |
| } |
| |
| /// <summary> |
| /// Returns true if an entry by the given name has already been written |
| /// to the ZipOutputStream. |
| /// </summary> |
| /// |
| /// <param name="name"> |
| /// The name of the entry to scan for. |
| /// </param> |
| /// |
| /// <returns> |
| /// true if an entry by the given name has already been written. |
| /// </returns> |
| public bool ContainsEntry(string name) |
| { |
| return _entriesWritten.ContainsKey(SharedUtilities.NormalizePathForUseInZipFile(name)); |
| } |
| |
| |
| /// <summary> |
| /// Write the data from the buffer to the stream. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// As the application writes data into this stream, the data may be |
| /// compressed and encrypted before being written out to the underlying |
| /// stream, depending on the settings of the <see cref="CompressionLevel"/> |
| /// and the <see cref="Encryption"/> properties. |
| /// </remarks> |
| /// |
| /// <param name="buffer">The buffer holding data to write to the stream.</param> |
| /// <param name="offset">the offset within that data array to find the first byte to write.</param> |
| /// <param name="count">the number of bytes to write.</param> |
| public override void Write(byte[] buffer, int offset, int count) |
| { |
| if (_disposed) |
| { |
| _exceptionPending = true; |
| throw new System.InvalidOperationException("The stream has been closed."); |
| } |
| |
| if (buffer==null) |
| { |
| _exceptionPending = true; |
| throw new System.ArgumentNullException("buffer"); |
| } |
| |
| if (_currentEntry == null) |
| { |
| _exceptionPending = true; |
| throw new System.InvalidOperationException("You must call PutNextEntry() before calling Write()."); |
| } |
| |
| if (_currentEntry.IsDirectory) |
| { |
| _exceptionPending = true; |
| throw new System.InvalidOperationException("You cannot Write() data for an entry that is a directory."); |
| } |
| |
| if (_needToWriteEntryHeader) |
| _InitiateCurrentEntry(false); |
| |
| if (count != 0) |
| _entryOutputStream.Write(buffer, offset, count); |
| } |
| |
| |
| |
| /// <summary> |
| /// Specify the name of the next entry that will be written to the zip file. |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// Call this method just before calling <see cref="Write(byte[], int, int)"/>, to |
| /// specify the name of the entry that the next set of bytes written to |
| /// the <c>ZipOutputStream</c> belongs to. All subsequent calls to <c>Write</c>, |
| /// until the next call to <c>PutNextEntry</c>, |
| /// will be inserted into the named entry in the zip file. |
| /// </para> |
| /// |
| /// <para> |
| /// If the <paramref name="entryName"/> used in <c>PutNextEntry()</c> ends in |
| /// a slash, then the entry added is marked as a directory. Because directory |
| /// entries do not contain data, a call to <c>Write()</c>, before an |
| /// intervening additional call to <c>PutNextEntry()</c>, will throw an |
| /// exception. |
| /// </para> |
| /// |
| /// <para> |
| /// If you don't call <c>Write()</c> between two calls to |
| /// <c>PutNextEntry()</c>, the first entry is inserted into the zip file as a |
| /// file of zero size. This may be what you want. |
| /// </para> |
| /// |
| /// <para> |
| /// Because <c>PutNextEntry()</c> closes out the prior entry, if any, this |
| /// method may throw if there is a problem with the prior entry. |
| /// </para> |
| /// |
| /// <para> |
| /// This method returns the <c>ZipEntry</c>. You can modify public properties |
| /// on the <c>ZipEntry</c>, such as <see cref="ZipEntry.Encryption"/>, <see |
| /// cref="ZipEntry.Password"/>, and so on, until the first call to |
| /// <c>ZipOutputStream.Write()</c>, or until the next call to |
| /// <c>PutNextEntry()</c>. If you modify the <c>ZipEntry</c> <em>after</em> |
| /// having called <c>Write()</c>, you may get a runtime exception, or you may |
| /// silently get an invalid zip archive. |
| /// </para> |
| /// |
| /// </remarks> |
| /// |
| /// <example> |
| /// |
| /// This example shows how to create a zip file, using the |
| /// <c>ZipOutputStream</c> class. |
| /// |
| /// <code> |
| /// private void Zipup() |
| /// { |
| /// using (FileStream fs raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite )) |
| /// { |
| /// using (var output= new ZipOutputStream(fs)) |
| /// { |
| /// output.Password = "VerySecret!"; |
| /// output.Encryption = EncryptionAlgorithm.WinZipAes256; |
| /// output.PutNextEntry("entry1.txt"); |
| /// byte[] buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #1."); |
| /// output.Write(buffer,0,buffer.Length); |
| /// output.PutNextEntry("entry2.txt"); // this will be zero length |
| /// output.PutNextEntry("entry3.txt"); |
| /// buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #3."); |
| /// output.Write(buffer,0,buffer.Length); |
| /// } |
| /// } |
| /// } |
| /// </code> |
| /// </example> |
| /// |
| /// <param name="entryName"> |
| /// The name of the entry to be added, including any path to be used |
| /// within the zip file. |
| /// </param> |
| /// |
| /// <returns> |
| /// The ZipEntry created. |
| /// </returns> |
| /// |
| public ZipEntry PutNextEntry(String entryName) |
| { |
| if (String.IsNullOrEmpty(entryName)) |
| throw new ArgumentNullException("entryName"); |
| |
| if (_disposed) |
| { |
| _exceptionPending = true; |
| throw new System.InvalidOperationException("The stream has been closed."); |
| } |
| |
| _FinishCurrentEntry(); |
| _currentEntry = ZipEntry.CreateForZipOutputStream(entryName); |
| _currentEntry._container = new ZipContainer(this); |
| _currentEntry._BitField |= 0x0008; // workitem 8932 |
| _currentEntry.SetEntryTimes(DateTime.Now, DateTime.Now, DateTime.Now); |
| _currentEntry.CompressionLevel = this.CompressionLevel; |
| _currentEntry.CompressionMethod = this.CompressionMethod; |
| _currentEntry.Password = _password; // workitem 13909 |
| _currentEntry.Encryption = this.Encryption; |
| // workitem 12634 |
| _currentEntry.AlternateEncoding = this.AlternateEncoding; |
| _currentEntry.AlternateEncodingUsage = this.AlternateEncodingUsage; |
| |
| if (entryName.EndsWith("/")) _currentEntry.MarkAsDirectory(); |
| |
| _currentEntry.EmitTimesInWindowsFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Windows) != 0); |
| _currentEntry.EmitTimesInUnixFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Unix) != 0); |
| InsureUniqueEntry(_currentEntry); |
| _needToWriteEntryHeader = true; |
| |
| return _currentEntry; |
| } |
| |
| |
| |
| private void _InitiateCurrentEntry(bool finishing) |
| { |
| // If finishing==true, this means we're initiating the entry at the time of |
| // Close() or PutNextEntry(). If this happens, it means no data was written |
| // for the entry - Write() was never called. (The usual case us to call |
| // _InitiateCurrentEntry(bool) from within Write().) If finishing==true, |
| // the entry could be either a zero-byte file or a directory. |
| |
| _entriesWritten.Add(_currentEntry.FileName,_currentEntry); |
| _entryCount++; // could use _entriesWritten.Count, but I don't want to incur |
| // the cost. |
| |
| if (_entryCount > 65534 && _zip64 == Zip64Option.Never) |
| { |
| _exceptionPending = true; |
| throw new System.InvalidOperationException("Too many entries. Consider setting ZipOutputStream.EnableZip64."); |
| } |
| |
| // Write out the header. |
| // |
| // If finishing, and encryption is in use, then we don't want to emit the |
| // normal encryption header. Signal that with a cycle=99 to turn off |
| // encryption for zero-byte entries or directories. |
| // |
| // If finishing, then we know the stream length is zero. Else, unknown |
| // stream length. Passing stream length == 0 allows an optimization so as |
| // not to setup an encryption or deflation stream, when stream length is |
| // zero. |
| |
| _currentEntry.WriteHeader(_outputStream, finishing ? 99 : 0); |
| _currentEntry.StoreRelativeOffset(); |
| |
| if (!_currentEntry.IsDirectory) |
| { |
| _currentEntry.WriteSecurityMetadata(_outputStream); |
| _currentEntry.PrepOutputStream(_outputStream, |
| finishing ? 0 : -1, |
| out _outputCounter, |
| out _encryptor, |
| out _deflater, |
| out _entryOutputStream); |
| } |
| _needToWriteEntryHeader = false; |
| } |
| |
| |
| |
| private void _FinishCurrentEntry() |
| { |
| if (_currentEntry != null) |
| { |
| if (_needToWriteEntryHeader) |
| _InitiateCurrentEntry(true); // an empty entry - no writes |
| |
| _currentEntry.FinishOutputStream(_outputStream, _outputCounter, _encryptor, _deflater, _entryOutputStream); |
| _currentEntry.PostProcessOutput(_outputStream); |
| // workitem 12964 |
| if (_currentEntry.OutputUsedZip64!=null) |
| _anyEntriesUsedZip64 |= _currentEntry.OutputUsedZip64.Value; |
| |
| // reset all the streams |
| _outputCounter = null; _encryptor = _deflater = null; _entryOutputStream = null; |
| } |
| } |
| |
| |
| |
| /// <summary> |
| /// Dispose the stream |
| /// </summary> |
| /// |
| /// <remarks> |
| /// <para> |
| /// This method writes the Zip Central directory, then closes the stream. The |
| /// application must call Dispose() (or Close) in order to produce a valid zip file. |
| /// </para> |
| /// |
| /// <para> |
| /// Typically the application will call <c>Dispose()</c> implicitly, via a <c>using</c> |
| /// statement in C#, or a <c>Using</c> statement in VB. |
| /// </para> |
| /// |
| /// </remarks> |
| /// |
| /// <param name="disposing">set this to true, always.</param> |
| protected override void Dispose(bool disposing) |
| { |
| if (_disposed) return; |
| |
| if (disposing) // not called from finalizer |
| { |
| // handle pending exceptions |
| if (!_exceptionPending) |
| { |
| _FinishCurrentEntry(); |
| _directoryNeededZip64 = ZipOutput.WriteCentralDirectoryStructure(_outputStream, |
| _entriesWritten.Values, |
| 1, // _numberOfSegmentsForMostRecentSave, |
| _zip64, |
| Comment, |
| new ZipContainer(this)); |
| Stream wrappedStream = null; |
| CountingStream cs = _outputStream as CountingStream; |
| if (cs != null) |
| { |
| wrappedStream = cs.WrappedStream; |
| #if NETCF |
| cs.Close(); |
| #else |
| cs.Dispose(); |
| #endif |
| } |
| else |
| { |
| wrappedStream = _outputStream; |
| } |
| |
| if (!_leaveUnderlyingStreamOpen) |
| { |
| #if NETCF |
| wrappedStream.Close(); |
| #else |
| wrappedStream.Dispose(); |
| #endif |
| } |
| _outputStream = null; |
| } |
| } |
| _disposed = true; |
| } |
| |
| |
| |
| /// <summary> |
| /// Always returns false. |
| /// </summary> |
| public override bool CanRead { get { return false; } } |
| |
| /// <summary> |
| /// Always returns false. |
| /// </summary> |
| public override bool CanSeek { get { return false; } } |
| |
| /// <summary> |
| /// Always returns true. |
| /// </summary> |
| public override bool CanWrite { get { return true; } } |
| |
| /// <summary> |
| /// Always returns a NotSupportedException. |
| /// </summary> |
| public override long Length { get { throw new NotSupportedException(); } } |
| |
| /// <summary> |
| /// Setting this property always returns a NotSupportedException. Getting it |
| /// returns the value of the Position on the underlying stream. |
| /// </summary> |
| public override long Position |
| { |
| get { return _outputStream.Position; } |
| set { throw new NotSupportedException(); } |
| } |
| |
| /// <summary> |
| /// This is a no-op. |
| /// </summary> |
| public override void Flush() { } |
| |
| /// <summary> |
| /// This method always throws a NotSupportedException. |
| /// </summary> |
| /// <param name="buffer">ignored</param> |
| /// <param name="offset">ignored</param> |
| /// <param name="count">ignored</param> |
| /// <returns>nothing</returns> |
| public override int Read(byte[] buffer, int offset, int count) |
| { |
| throw new NotSupportedException("Read"); |
| } |
| |
| /// <summary> |
| /// This method always throws a NotSupportedException. |
| /// </summary> |
| /// <param name="offset">ignored</param> |
| /// <param name="origin">ignored</param> |
| /// <returns>nothing</returns> |
| public override long Seek(long offset, SeekOrigin origin) |
| { |
| throw new NotSupportedException("Seek"); |
| } |
| |
| /// <summary> |
| /// This method always throws a NotSupportedException. |
| /// </summary> |
| /// <param name="value">ignored</param> |
| public override void SetLength(long value) |
| { |
| throw new NotSupportedException(); |
| } |
| |
| |
| private EncryptionAlgorithm _encryption; |
| private ZipEntryTimestamp _timestamp; |
| internal String _password; |
| private String _comment; |
| private Stream _outputStream; |
| private ZipEntry _currentEntry; |
| internal Zip64Option _zip64; |
| private Dictionary<String, ZipEntry> _entriesWritten; |
| private int _entryCount; |
| private ZipOption _alternateEncodingUsage = ZipOption.Never; |
| private System.Text.Encoding _alternateEncoding |
| = System.Text.Encoding.GetEncoding("IBM437"); // default = IBM437 |
| |
| private bool _leaveUnderlyingStreamOpen; |
| private bool _disposed; |
| private bool _exceptionPending; // **see note below |
| private bool _anyEntriesUsedZip64, _directoryNeededZip64; |
| private CountingStream _outputCounter; |
| private Stream _encryptor; |
| private Stream _deflater; |
| private Ionic.Crc.CrcCalculatorStream _entryOutputStream; |
| private bool _needToWriteEntryHeader; |
| private string _name; |
| private bool _DontIgnoreCase; |
| #if !NETCF |
| internal ParallelDeflateOutputStream ParallelDeflater; |
| private long _ParallelDeflateThreshold; |
| private int _maxBufferPairs = 16; |
| #endif |
| |
| // **Note regarding exceptions: |
| |
| // When ZipOutputStream is employed within a using clause, which |
| // is the typical scenario, and an exception is thrown within |
| // the scope of the using, Close()/Dispose() is invoked |
| // implicitly before processing the initial exception. In that |
| // case, _exceptionPending is true, and we don't want to try to |
| // write anything in the Close/Dispose logic. Doing so can |
| // cause additional exceptions that mask the original one. So, |
| // the _exceptionPending flag is used to track that, and to |
| // allow the original exception to be propagated to the |
| // application without extra "noise." |
| |
| } |
| |
| |
| |
| internal class ZipContainer |
| { |
| private ZipFile _zf; |
| private ZipOutputStream _zos; |
| private ZipInputStream _zis; |
| |
| public ZipContainer(Object o) |
| { |
| _zf = (o as ZipFile); |
| _zos = (o as ZipOutputStream); |
| _zis = (o as ZipInputStream); |
| } |
| |
| public ZipFile ZipFile |
| { |
| get { return _zf; } |
| } |
| |
| public ZipOutputStream ZipOutputStream |
| { |
| get { return _zos; } |
| } |
| |
| public string Name |
| { |
| get |
| { |
| if (_zf != null) return _zf.Name; |
| if (_zis != null) throw new NotSupportedException(); |
| return _zos.Name; |
| } |
| } |
| |
| public string Password |
| { |
| get |
| { |
| if (_zf != null) return _zf._Password; |
| if (_zis != null) return _zis._Password; |
| return _zos._password; |
| } |
| } |
| |
| public Zip64Option Zip64 |
| { |
| get |
| { |
| if (_zf != null) return _zf._zip64; |
| if (_zis != null) throw new NotSupportedException(); |
| return _zos._zip64; |
| } |
| } |
| |
| public int BufferSize |
| { |
| get |
| { |
| if (_zf != null) return _zf.BufferSize; |
| if (_zis != null) throw new NotSupportedException(); |
| return 0; |
| } |
| } |
| |
| #if !NETCF |
| public Ionic.Zlib.ParallelDeflateOutputStream ParallelDeflater |
| { |
| get |
| { |
| if (_zf != null) return _zf.ParallelDeflater; |
| if (_zis != null) return null; |
| return _zos.ParallelDeflater; |
| } |
| set |
| { |
| if (_zf != null) _zf.ParallelDeflater = value; |
| else if (_zos != null) _zos.ParallelDeflater = value; |
| } |
| } |
| |
| public long ParallelDeflateThreshold |
| { |
| get |
| { |
| if (_zf != null) return _zf.ParallelDeflateThreshold; |
| return _zos.ParallelDeflateThreshold; |
| } |
| } |
| public int ParallelDeflateMaxBufferPairs |
| { |
| get |
| { |
| if (_zf != null) return _zf.ParallelDeflateMaxBufferPairs; |
| return _zos.ParallelDeflateMaxBufferPairs; |
| } |
| } |
| #endif |
| |
| public int CodecBufferSize |
| { |
| get |
| { |
| if (_zf != null) return _zf.CodecBufferSize; |
| if (_zis != null) return _zis.CodecBufferSize; |
| return _zos.CodecBufferSize; |
| } |
| } |
| |
| public Ionic.Zlib.CompressionStrategy Strategy |
| { |
| get |
| { |
| if (_zf != null) return _zf.Strategy; |
| return _zos.Strategy; |
| } |
| } |
| |
| public Zip64Option UseZip64WhenSaving |
| { |
| get |
| { |
| if (_zf != null) return _zf.UseZip64WhenSaving; |
| return _zos.EnableZip64; |
| } |
| } |
| |
| public System.Text.Encoding AlternateEncoding |
| { |
| get |
| { |
| if (_zf != null) return _zf.AlternateEncoding; |
| if (_zos!=null) return _zos.AlternateEncoding; |
| return null; |
| } |
| } |
| public System.Text.Encoding DefaultEncoding |
| { |
| get |
| { |
| if (_zf != null) return ZipFile.DefaultEncoding; |
| if (_zos!=null) return ZipOutputStream.DefaultEncoding; |
| return null; |
| } |
| } |
| public ZipOption AlternateEncodingUsage |
| { |
| get |
| { |
| if (_zf != null) return _zf.AlternateEncodingUsage; |
| if (_zos!=null) return _zos.AlternateEncodingUsage; |
| return ZipOption.Never; // n/a |
| } |
| } |
| |
| public Stream ReadStream |
| { |
| get |
| { |
| if (_zf != null) return _zf.ReadStream; |
| return _zis.ReadStream; |
| } |
| } |
| } |
| |
| } |