Free cookie consent management tool by TermsFeed Policy Generator

source: branches/RemoveBackwardsCompatibility/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/Packaging/DotNetZip/ZipOutputStream.cs @ 13401

Last change on this file since 13401 was 12074, checked in by sraggl, 9 years ago

#2341: Added EPPlus-4.0.3 to ExtLibs

File size: 75.0 KB
Line 
1// ZipOutputStream.cs
2//
3// ------------------------------------------------------------------
4//
5// Copyright (c) 2009 Dino Chiesa.
6// All rights reserved.
7//
8// This code module is part of DotNetZip, a zipfile class library.
9//
10// ------------------------------------------------------------------
11//
12// This code is licensed under the Microsoft Public License.
13// See the file License.txt for the license details.
14// More info on: http://dotnetzip.codeplex.com
15//
16// ------------------------------------------------------------------
17//
18// last saved (in emacs):
19// Time-stamp: <2011-July-28 06:34:30>
20//
21// ------------------------------------------------------------------
22//
23// This module defines the ZipOutputStream class, which is a stream metaphor for
24// generating zip files.  This class does not depend on Ionic.Zip.ZipFile, but rather
25// stands alongside it as an alternative "container" for ZipEntry.  It replicates a
26// subset of the properties, including these:
27//
28//  - Comment
29//  - Encryption
30//  - Password
31//  - CodecBufferSize
32//  - CompressionLevel
33//  - CompressionMethod
34//  - EnableZip64 (UseZip64WhenSaving)
35//  - IgnoreCase (!CaseSensitiveRetrieval)
36//
37// It adds these novel methods:
38//
39//  - PutNextEntry
40//
41//
42// ------------------------------------------------------------------
43//
44
45using System;
46using System.Threading;
47using System.Collections.Generic;
48using System.IO;
49using Ionic.Zip;
50using OfficeOpenXml.Packaging.Ionic.Zlib;
51
52namespace OfficeOpenXml.Packaging.Ionic.Zip
53{
54    /// <summary>
55    ///   Provides a stream metaphor for generating zip files.
56    /// </summary>
57    ///
58    /// <remarks>
59    /// <para>
60    ///   This class writes zip files, as defined in the <see
61    ///   href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">specification
62    ///   for zip files described by PKWare</see>.  The compression for this
63    ///   implementation is provided by a managed-code version of Zlib, included with
64    ///   DotNetZip in the classes in the Ionic.Zlib namespace.
65    /// </para>
66    ///
67    /// <para>
68    ///   This class provides an alternative programming model to the one enabled by the
69    ///   <see cref="ZipFile"/> class. Use this when creating zip files, as an
70    ///   alternative to the <see cref="ZipFile"/> class, when you would like to use a
71    ///   <c>Stream</c> type to write the zip file.
72    /// </para>
73    ///
74    /// <para>
75    ///   Both the <c>ZipOutputStream</c> class and the <c>ZipFile</c> class can be used
76    ///   to create zip files. Both of them support many of the common zip features,
77    ///   including Unicode, different compression levels, and ZIP64.   They provide
78    ///   very similar performance when creating zip files.
79    /// </para>
80    ///
81    /// <para>
82    ///   The <c>ZipFile</c> class is generally easier to use than
83    ///   <c>ZipOutputStream</c> and should be considered a higher-level interface.  For
84    ///   example, when creating a zip file via calls to the <c>PutNextEntry()</c> and
85    ///   <c>Write()</c> methods on the <c>ZipOutputStream</c> class, the caller is
86    ///   responsible for opening the file, reading the bytes from the file, writing
87    ///   those bytes into the <c>ZipOutputStream</c>, setting the attributes on the
88    ///   <c>ZipEntry</c>, and setting the created, last modified, and last accessed
89    ///   timestamps on the zip entry. All of these things are done automatically by a
90    ///   call to <see cref="ZipFile.AddFile(string,string)">ZipFile.AddFile()</see>.
91    ///   For this reason, the <c>ZipOutputStream</c> is generally recommended for use
92    ///   only when your application emits arbitrary data, not necessarily data from a
93    ///   filesystem file, directly into a zip file, and does so using a <c>Stream</c>
94    ///   metaphor.
95    /// </para>
96    ///
97    /// <para>
98    ///   Aside from the differences in programming model, there are other
99    ///   differences in capability between the two classes.
100    /// </para>
101    ///
102    /// <list type="bullet">
103    ///   <item>
104    ///     <c>ZipFile</c> can be used to read and extract zip files, in addition to
105    ///     creating zip files. <c>ZipOutputStream</c> cannot read zip files. If you want
106    ///     to use a stream to read zip files, check out the <see cref="ZipInputStream"/> class.
107    ///   </item>
108    ///
109    ///   <item>
110    ///     <c>ZipOutputStream</c> does not support the creation of segmented or spanned
111    ///     zip files.
112    ///   </item>
113    ///
114    ///   <item>
115    ///     <c>ZipOutputStream</c> cannot produce a self-extracting archive.
116    ///   </item>
117    /// </list>
118    ///
119    /// <para>
120    ///   Be aware that the <c>ZipOutputStream</c> class implements the <see
121    ///   cref="System.IDisposable"/> interface.  In order for
122    ///   <c>ZipOutputStream</c> to produce a valid zip file, you use use it within
123    ///   a using clause (<c>Using</c> in VB), or call the <c>Dispose()</c> method
124    ///   explicitly.  See the examples for how to employ a using clause.
125    /// </para>
126    ///
127    /// <para>
128    ///   Also, a note regarding compression performance: On the desktop .NET
129    ///   Framework, DotNetZip can use a multi-threaded compression implementation
130    ///   that provides significant speed increases on large files, over 300k or so,
131    ///   at the cost of increased memory use at runtime.  (The output of the
132    ///   compression is almost exactly the same size).  But, the multi-threaded
133    ///   approach incurs a performance hit on smaller files. There's no way for the
134    ///   ZipOutputStream to know whether parallel compression will be beneficial,
135    ///   because the ZipOutputStream does not know how much data you will write
136    ///   through the stream.  You may wish to set the <see
137    ///   cref="ParallelDeflateThreshold"/> property to zero, if you are compressing
138    ///   large files through <c>ZipOutputStream</c>.  This will cause parallel
139    ///   compression to be used, always.
140    /// </para>
141    /// </remarks>
142    internal class ZipOutputStream : Stream
143    {
144        /// <summary>
145        ///   Create a ZipOutputStream, wrapping an existing stream.
146        /// </summary>
147        ///
148        /// <remarks>
149        /// <para>
150        ///   The <see cref="ZipFile"/> class is generally easier to use when creating
151        ///   zip files. The ZipOutputStream offers a different metaphor for creating a
152        ///   zip file, based on the <see cref="System.IO.Stream"/> class.
153        /// </para>
154        ///
155        /// </remarks>
156        ///
157        /// <param name="stream">
158        /// The stream to wrap. It must be writable. This stream will be closed at
159        /// the time the ZipOutputStream is closed.
160        /// </param>
161        ///
162        /// <example>
163        ///
164        ///   This example shows how to create a zip file, using the
165        ///   ZipOutputStream class.
166        ///
167        /// <code lang="C#">
168        /// private void Zipup()
169        /// {
170        ///     if (filesToZip.Count == 0)
171        ///     {
172        ///         System.Console.WriteLine("Nothing to do.");
173        ///         return;
174        ///     }
175        ///
176        ///     using (var raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite ))
177        ///     {
178        ///         using (var output= new ZipOutputStream(raw))
179        ///         {
180        ///             output.Password = "VerySecret!";
181        ///             output.Encryption = EncryptionAlgorithm.WinZipAes256;
182        ///
183        ///             foreach (string inputFileName in filesToZip)
184        ///             {
185        ///                 System.Console.WriteLine("file: {0}", inputFileName);
186        ///
187        ///                 output.PutNextEntry(inputFileName);
188        ///                 using (var input = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Write ))
189        ///                 {
190        ///                     byte[] buffer= new byte[2048];
191        ///                     int n;
192        ///                     while ((n= input.Read(buffer,0,buffer.Length)) > 0)
193        ///                     {
194        ///                         output.Write(buffer,0,n);
195        ///                     }
196        ///                 }
197        ///             }
198        ///         }
199        ///     }
200        /// }
201        /// </code>
202        ///
203        /// <code lang="VB">
204        /// Private Sub Zipup()
205        ///     Dim outputFileName As String = "XmlData.zip"
206        ///     Dim filesToZip As String() = Directory.GetFiles(".", "*.xml")
207        ///     If (filesToZip.Length = 0) Then
208        ///         Console.WriteLine("Nothing to do.")
209        ///     Else
210        ///         Using raw As FileStream = File.Open(outputFileName, FileMode.Create, FileAccess.ReadWrite)
211        ///             Using output As ZipOutputStream = New ZipOutputStream(raw)
212        ///                 output.Password = "VerySecret!"
213        ///                 output.Encryption = EncryptionAlgorithm.WinZipAes256
214        ///                 Dim inputFileName As String
215        ///                 For Each inputFileName In filesToZip
216        ///                     Console.WriteLine("file: {0}", inputFileName)
217        ///                     output.PutNextEntry(inputFileName)
218        ///                     Using input As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
219        ///                         Dim n As Integer
220        ///                         Dim buffer As Byte() = New Byte(2048) {}
221        ///                         Do While (n = input.Read(buffer, 0, buffer.Length) > 0)
222        ///                             output.Write(buffer, 0, n)
223        ///                         Loop
224        ///                     End Using
225        ///                 Next
226        ///             End Using
227        ///         End Using
228        ///     End If
229        /// End Sub
230        /// </code>
231        /// </example>
232        public ZipOutputStream(Stream stream) : this(stream, false) { }
233
234
235        /// <summary>
236        ///   Create a ZipOutputStream that writes to a filesystem file.
237        /// </summary>
238        ///
239        /// <remarks>
240        ///   The <see cref="ZipFile"/> class is generally easier to use when creating
241        ///   zip files. The ZipOutputStream offers a different metaphor for creating a
242        ///   zip file, based on the <see cref="System.IO.Stream"/> class.
243        /// </remarks>
244        ///
245        /// <param name="fileName">
246        ///   The name of the zip file to create.
247        /// </param>
248        ///
249        /// <example>
250        ///
251        ///   This example shows how to create a zip file, using the
252        ///   ZipOutputStream class.
253        ///
254        /// <code lang="C#">
255        /// private void Zipup()
256        /// {
257        ///     if (filesToZip.Count == 0)
258        ///     {
259        ///         System.Console.WriteLine("Nothing to do.");
260        ///         return;
261        ///     }
262        ///
263        ///     using (var output= new ZipOutputStream(outputFileName))
264        ///     {
265        ///         output.Password = "VerySecret!";
266        ///         output.Encryption = EncryptionAlgorithm.WinZipAes256;
267        ///
268        ///         foreach (string inputFileName in filesToZip)
269        ///         {
270        ///             System.Console.WriteLine("file: {0}", inputFileName);
271        ///
272        ///             output.PutNextEntry(inputFileName);
273        ///             using (var input = File.Open(inputFileName, FileMode.Open, FileAccess.Read,
274        ///                                          FileShare.Read | FileShare.Write ))
275        ///             {
276        ///                 byte[] buffer= new byte[2048];
277        ///                 int n;
278        ///                 while ((n= input.Read(buffer,0,buffer.Length)) > 0)
279        ///                 {
280        ///                     output.Write(buffer,0,n);
281        ///                 }
282        ///             }
283        ///         }
284        ///     }
285        /// }
286        /// </code>
287        ///
288        /// <code lang="VB">
289        /// Private Sub Zipup()
290        ///     Dim outputFileName As String = "XmlData.zip"
291        ///     Dim filesToZip As String() = Directory.GetFiles(".", "*.xml")
292        ///     If (filesToZip.Length = 0) Then
293        ///         Console.WriteLine("Nothing to do.")
294        ///     Else
295        ///         Using output As ZipOutputStream = New ZipOutputStream(outputFileName)
296        ///             output.Password = "VerySecret!"
297        ///             output.Encryption = EncryptionAlgorithm.WinZipAes256
298        ///             Dim inputFileName As String
299        ///             For Each inputFileName In filesToZip
300        ///                 Console.WriteLine("file: {0}", inputFileName)
301        ///                 output.PutNextEntry(inputFileName)
302        ///                 Using input As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
303        ///                     Dim n As Integer
304        ///                     Dim buffer As Byte() = New Byte(2048) {}
305        ///                     Do While (n = input.Read(buffer, 0, buffer.Length) > 0)
306        ///                         output.Write(buffer, 0, n)
307        ///                     Loop
308        ///                 End Using
309        ///             Next
310        ///         End Using
311        ///     End If
312        /// End Sub
313        /// </code>
314        /// </example>
315        public ZipOutputStream(String fileName)
316        {
317            Stream stream = File.Open(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
318            _Init(stream, false, fileName);
319        }
320
321
322        /// <summary>
323        ///   Create a ZipOutputStream.
324        /// </summary>
325        ///
326        /// <remarks>
327        ///   See the documentation for the <see
328        ///   cref="ZipOutputStream(Stream)">ZipOutputStream(Stream)</see>
329        ///   constructor for an example.
330        /// </remarks>
331        ///
332        /// <param name="stream">
333        ///   The stream to wrap. It must be writable.
334        /// </param>
335        ///
336        /// <param name="leaveOpen">
337        ///   true if the application would like the stream
338        ///   to remain open after the <c>ZipOutputStream</c> has been closed.
339        /// </param>
340        public ZipOutputStream(Stream stream, bool leaveOpen)
341        {
342            _Init(stream, leaveOpen, null);
343        }
344
345        private void _Init(Stream stream, bool leaveOpen, string name)
346        {
347            // workitem 9307
348            _outputStream = stream.CanRead ? stream : new CountingStream(stream);
349            CompressionLevel = OfficeOpenXml.Packaging.Ionic.Zlib.CompressionLevel.Default;
350            CompressionMethod = OfficeOpenXml.Packaging.Ionic.Zip.CompressionMethod.Deflate;
351            _encryption = EncryptionAlgorithm.None;
352            _entriesWritten = new Dictionary<String, ZipEntry>(StringComparer.Ordinal);
353            _zip64 = Zip64Option.Never;
354            _leaveUnderlyingStreamOpen = leaveOpen;
355            Strategy = Ionic.Zlib.CompressionStrategy.Default;
356            _name = name ?? "(stream)";
357#if !NETCF
358            ParallelDeflateThreshold = -1L;
359#endif
360        }
361
362
363        /// <summary>Provides a string representation of the instance.</summary>
364        /// <remarks>
365        ///   <para>
366        ///     This can be useful for debugging purposes.
367        ///   </para>
368        /// </remarks>
369        /// <returns>a string representation of the instance.</returns>
370        public override String ToString()
371        {
372            return String.Format ("ZipOutputStream::{0}(leaveOpen({1})))", _name, _leaveUnderlyingStreamOpen);
373        }
374
375
376        /// <summary>
377        ///   Sets the password to be used on the <c>ZipOutputStream</c> instance.
378        /// </summary>
379        ///
380        /// <remarks>
381        ///
382        /// <para>
383        ///   When writing a zip archive, this password is applied to the entries, not
384        ///   to the zip archive itself. It applies to any <c>ZipEntry</c> subsequently
385        ///   written to the <c>ZipOutputStream</c>.
386        /// </para>
387        ///
388        /// <para>
389        ///   Using a password does not encrypt or protect the "directory" of the
390        ///   archive - the list of entries contained in the archive.  If you set the
391        ///   <c>Password</c> property, the password actually applies to individual
392        ///   entries that are added to the archive, subsequent to the setting of this
393        ///   property.  The list of filenames in the archive that is eventually created
394        ///   will appear in clear text, but the contents of the individual files are
395        ///   encrypted.  This is how Zip encryption works.
396        /// </para>
397        ///
398        /// <para>
399        ///   If you set this property, and then add a set of entries to the archive via
400        ///   calls to <c>PutNextEntry</c>, then each entry is encrypted with that
401        ///   password.  You may also want to change the password between adding
402        ///   different entries. If you set the password, add an entry, then set the
403        ///   password to <c>null</c> (<c>Nothing</c> in VB), and add another entry, the
404        ///   first entry is encrypted and the second is not.
405        /// </para>
406        ///
407        /// <para>
408        ///   When setting the <c>Password</c>, you may also want to explicitly set the <see
409        ///   cref="Encryption"/> property, to specify how to encrypt the entries added
410        ///   to the ZipFile.  If you set the <c>Password</c> to a non-null value and do not
411        ///   set <see cref="Encryption"/>, then PKZip 2.0 ("Weak") encryption is used.
412        ///   This encryption is relatively weak but is very interoperable. If
413        ///   you set the password to a <c>null</c> value (<c>Nothing</c> in VB),
414        ///   <c>Encryption</c> is reset to None.
415        /// </para>
416        ///
417        /// <para>
418        ///   Special case: if you wrap a ZipOutputStream around a non-seekable stream,
419        ///   and use encryption, and emit an entry of zero bytes, the <c>Close()</c> or
420        ///   <c>PutNextEntry()</c> following the entry will throw an exception.
421        /// </para>
422        ///
423        /// </remarks>
424        public String Password
425        {
426            set
427            {
428                if (_disposed)
429                {
430                    _exceptionPending = true;
431                    throw new System.InvalidOperationException("The stream has been closed.");
432                }
433
434                _password = value;
435                if (_password == null)
436                {
437                    _encryption = EncryptionAlgorithm.None;
438                }
439                else if (_encryption == EncryptionAlgorithm.None)
440                {
441                    _encryption = EncryptionAlgorithm.PkzipWeak;
442                }
443            }
444        }
445
446
447        /// <summary>
448        ///   The Encryption to use for entries added to the <c>ZipOutputStream</c>.
449        /// </summary>
450        ///
451        /// <remarks>
452        /// <para>
453        ///   The specified Encryption is applied to the entries subsequently
454        ///   written to the <c>ZipOutputStream</c> instance.
455        /// </para>
456        ///
457        /// <para>
458        ///   If you set this to something other than
459        ///   EncryptionAlgorithm.None, you will also need to set the
460        ///   <see cref="Password"/> to a non-null, non-empty value in
461        ///   order to actually get encryption on the entry.
462        /// </para>
463        ///
464        /// </remarks>
465        ///
466        /// <seealso cref="Password">ZipOutputStream.Password</seealso>
467        /// <seealso cref="ZipEntry.Encryption">ZipEntry.Encryption</seealso>
468        public EncryptionAlgorithm Encryption
469        {
470            get
471            {
472                return _encryption;
473            }
474            set
475            {
476                if (_disposed)
477                {
478                    _exceptionPending = true;
479                    throw new System.InvalidOperationException("The stream has been closed.");
480                }
481                if (value == EncryptionAlgorithm.Unsupported)
482                {
483                    _exceptionPending = true;
484                    throw new InvalidOperationException("You may not set Encryption to that value.");
485                }
486                _encryption = value;
487            }
488        }
489
490
491        /// <summary>
492        ///   Size of the work buffer to use for the ZLIB codec during compression.
493        /// </summary>
494        ///
495        /// <remarks>
496        ///   Setting this may affect performance.  For larger files, setting this to a
497        ///   larger size may improve performance, but I'm not sure.  Sorry, I don't
498        ///   currently have good recommendations on how to set it.  You can test it if
499        ///   you like.
500        /// </remarks>
501        public int CodecBufferSize
502        {
503            get;
504            set;
505        }
506
507
508        /// <summary>
509        ///   The compression strategy to use for all entries.
510        /// </summary>
511        ///
512        /// <remarks>
513        ///   Set the Strategy used by the ZLIB-compatible compressor, when compressing
514        ///   data for the entries in the zip archive. Different compression strategies
515        ///   work better on different sorts of data. The strategy parameter can affect
516        ///   the compression ratio and the speed of compression but not the correctness
517        ///   of the compresssion.  For more information see <see
518        ///   cref="Ionic.Zlib.CompressionStrategy "/>.
519        /// </remarks>
520        public CompressionStrategy Strategy
521        {
522            get;
523            set;
524        }
525
526
527        /// <summary>
528        ///   The type of timestamp attached to the ZipEntry.
529        /// </summary>
530        ///
531        /// <remarks>
532        ///   Set this in order to specify the kind of timestamp that should be emitted
533        ///   into the zip file for each entry.
534        /// </remarks>
535        public ZipEntryTimestamp Timestamp
536        {
537            get
538            {
539                return _timestamp;
540            }
541            set
542            {
543                if (_disposed)
544                {
545                    _exceptionPending = true;
546                    throw new System.InvalidOperationException("The stream has been closed.");
547                }
548                _timestamp = value;
549            }
550        }
551
552
553        /// <summary>
554        ///   Sets the compression level to be used for entries subsequently added to
555        ///   the zip archive.
556        /// </summary>
557        ///
558        /// <remarks>
559        ///  <para>
560        ///    Varying the compression level used on entries can affect the
561        ///    size-vs-speed tradeoff when compression and decompressing data streams
562        ///    or files.
563        ///  </para>
564        ///
565        ///  <para>
566        ///    As with some other properties on the <c>ZipOutputStream</c> class, like <see
567        ///    cref="Password"/>, and <see cref="Encryption"/>,
568        ///    setting this property on a <c>ZipOutputStream</c>
569        ///    instance will cause the specified <c>CompressionLevel</c> to be used on all
570        ///    <see cref="ZipEntry"/> items that are subsequently added to the
571        ///    <c>ZipOutputStream</c> instance.
572        ///  </para>
573        ///
574        ///  <para>
575        ///    If you do not set this property, the default compression level is used,
576        ///    which normally gives a good balance of compression efficiency and
577        ///    compression speed.  In some tests, using <c>BestCompression</c> can
578        ///    double the time it takes to compress, while delivering just a small
579        ///    increase in compression efficiency.  This behavior will vary with the
580        ///    type of data you compress.  If you are in doubt, just leave this setting
581        ///    alone, and accept the default.
582        ///  </para>
583        /// </remarks>
584        public OfficeOpenXml.Packaging.Ionic.Zlib.CompressionLevel CompressionLevel
585        {
586            get;
587            set;
588        }
589
590        /// <summary>
591        ///   The compression method used on each entry added to the ZipOutputStream.
592        /// </summary>
593        public CompressionMethod CompressionMethod
594        {
595            get;
596            set;
597        }
598
599
600        /// <summary>
601        ///   A comment attached to the zip archive.
602        /// </summary>
603        ///
604        /// <remarks>
605        ///
606        /// <para>
607        ///   The application sets this property to specify a comment to be embedded
608        ///   into the generated zip archive.
609        /// </para>
610        ///
611        /// <para>
612        ///   According to <see
613        ///   href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">PKWARE's
614        ///   zip specification</see>, the comment is not encrypted, even if there is a
615        ///   password set on the zip file.
616        /// </para>
617        ///
618        /// <para>
619        ///   The specification does not describe how to indicate the encoding used
620        ///   on a comment string. Many "compliant" zip tools and libraries use
621        ///   IBM437 as the code page for comments; DotNetZip, too, follows that
622        ///   practice.  On the other hand, there are situations where you want a
623        ///   Comment to be encoded with something else, for example using code page
624        ///   950 "Big-5 Chinese". To fill that need, DotNetZip will encode the
625        ///   comment following the same procedure it follows for encoding
626        ///   filenames: (a) if <see cref="AlternateEncodingUsage"/> is
627        ///   <c>Never</c>, it uses the default encoding (IBM437). (b) if <see
628        ///   cref="AlternateEncodingUsage"/> is <c>Always</c>, it always uses the
629        ///   alternate encoding (<see cref="AlternateEncoding"/>). (c) if <see
630        ///   cref="AlternateEncodingUsage"/> is <c>AsNecessary</c>, it uses the
631        ///   alternate encoding only if the default encoding is not sufficient for
632        ///   encoding the comment - in other words if decoding the result does not
633        ///   produce the original string.  This decision is taken at the time of
634        ///   the call to <c>ZipFile.Save()</c>.
635        /// </para>
636        ///
637        /// </remarks>
638        public string Comment
639        {
640            get { return _comment; }
641            set
642            {
643                if (_disposed)
644                {
645                    _exceptionPending = true;
646                    throw new System.InvalidOperationException("The stream has been closed.");
647                }
648                _comment = value;
649            }
650        }
651
652
653
654        /// <summary>
655        ///   Specify whether to use ZIP64 extensions when saving a zip archive.
656        /// </summary>
657        ///
658        /// <remarks>
659        /// <para>
660        ///   The default value for the property is <see
661        ///   cref="Zip64Option.Never"/>. <see cref="Zip64Option.AsNecessary"/> is
662        ///   safest, in the sense that you will not get an Exception if a
663        ///   pre-ZIP64 limit is exceeded.
664        /// </para>
665        ///
666        /// <para>
667        ///   You must set this property before calling <c>Write()</c>.
668        /// </para>
669        ///
670        /// </remarks>
671        public Zip64Option EnableZip64
672        {
673            get
674            {
675                return _zip64;
676            }
677            set
678            {
679                if (_disposed)
680                {
681                    _exceptionPending = true;
682                    throw new System.InvalidOperationException("The stream has been closed.");
683                }
684                _zip64 = value;
685            }
686        }
687
688
689        /// <summary>
690        ///   Indicates whether ZIP64 extensions were used when saving the zip archive.
691        /// </summary>
692        ///
693        /// <remarks>
694        ///   The value is defined only after the <c>ZipOutputStream</c> has been closed.
695        /// </remarks>
696        public bool OutputUsedZip64
697        {
698            get
699            {
700                return _anyEntriesUsedZip64 || _directoryNeededZip64;
701            }
702        }
703
704
705        /// <summary>
706        ///   Whether the ZipOutputStream should use case-insensitive comparisons when
707        ///   checking for uniqueness of zip entries.
708        /// </summary>
709        ///
710        /// <remarks>
711        ///   <para>
712        ///   Though the zip specification doesn't prohibit zipfiles with duplicate
713        ///   entries, Sane zip files have no duplicates, and the DotNetZip library
714        ///   cannot create zip files with duplicate entries. If an application attempts
715        ///   to call <see cref="PutNextEntry(String)"/> with a name that duplicates one
716        ///   already used within the archive, the library will throw an Exception.
717        ///   </para>
718        ///   <para>
719        ///   This property allows the application to specify whether the
720        ///   ZipOutputStream instance considers ordinal case when checking for
721        ///   uniqueness of zip entries.
722        ///   </para>
723        /// </remarks>
724        public bool IgnoreCase
725        {
726          get
727          {
728              return !_DontIgnoreCase;
729          }
730
731          set
732          {
733              _DontIgnoreCase = !value;
734          }
735
736        }
737
738
739        /// <summary>
740        ///   Indicates whether to encode entry filenames and entry comments using
741        ///   Unicode (UTF-8).
742        /// </summary>
743        ///
744        /// <remarks>
745        /// <para>
746        ///   <see href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">The
747        ///   PKWare zip specification</see> provides for encoding file names and file
748        ///   comments in either the IBM437 code page, or in UTF-8.  This flag selects
749        ///   the encoding according to that specification.  By default, this flag is
750        ///   false, and filenames and comments are encoded into the zip file in the
751        ///   IBM437 codepage.  Setting this flag to true will specify that filenames
752        ///   and comments that cannot be encoded with IBM437 will be encoded with
753        ///   UTF-8.
754        /// </para>
755        ///
756        /// <para>
757        ///   Zip files created with strict adherence to the PKWare specification with
758        ///   respect to UTF-8 encoding can contain entries with filenames containing
759        ///   any combination of Unicode characters, including the full range of
760        ///   characters from Chinese, Latin, Hebrew, Greek, Cyrillic, and many other
761        ///   alphabets.  However, because at this time, the UTF-8 portion of the PKWare
762        ///   specification is not broadly supported by other zip libraries and
763        ///   utilities, such zip files may not be readable by your favorite zip tool or
764        ///   archiver. In other words, interoperability will decrease if you set this
765        ///   flag to true.
766        /// </para>
767        ///
768        /// <para>
769        ///   In particular, Zip files created with strict adherence to the PKWare
770        ///   specification with respect to UTF-8 encoding will not work well with
771        ///   Explorer in Windows XP or Windows Vista, because Windows compressed
772        ///   folders, as far as I know, do not support UTF-8 in zip files.  Vista can
773        ///   read the zip files, but shows the filenames incorrectly. Unpacking from
774        ///   Windows Vista Explorer will result in filenames that have rubbish
775        ///   characters in place of the high-order UTF-8 bytes.
776        /// </para>
777        ///
778        /// <para>
779        ///   Also, zip files that use UTF-8 encoding will not work well with Java
780        ///   applications that use the java.util.zip classes, as of v5.0 of the Java
781        ///   runtime. The Java runtime does not correctly implement the PKWare
782        ///   specification in this regard.
783        /// </para>
784        ///
785        /// <para>
786        ///   As a result, we have the unfortunate situation that "correct" behavior by
787        ///   the DotNetZip library with regard to Unicode encoding of filenames during
788        ///   zip creation will result in zip files that are readable by strictly
789        ///   compliant and current tools (for example the most recent release of the
790        ///   commercial WinZip tool); but these zip files will not be readable by
791        ///   various other tools or libraries, including Windows Explorer.
792        /// </para>
793        ///
794        /// <para>
795        ///   The DotNetZip library can read and write zip files with UTF8-encoded
796        ///   entries, according to the PKware spec.  If you use DotNetZip for both
797        ///   creating and reading the zip file, and you use UTF-8, there will be no
798        ///   loss of information in the filenames. For example, using a self-extractor
799        ///   created by this library will allow you to unpack files correctly with no
800        ///   loss of information in the filenames.
801        /// </para>
802        ///
803        /// <para>
804        ///   If you do not set this flag, it will remain false.  If this flag is false,
805        ///   the <c>ZipOutputStream</c> will encode all filenames and comments using
806        ///   the IBM437 codepage.  This can cause "loss of information" on some
807        ///   filenames, but the resulting zipfile will be more interoperable with other
808        ///   utilities. As an example of the loss of information, diacritics can be
809        ///   lost.  The o-tilde character will be down-coded to plain o.  The c with a
810        ///   cedilla (Unicode 0xE7) used in Portugese will be downcoded to a c.
811        ///   Likewise, the O-stroke character (Unicode 248), used in Danish and
812        ///   Norwegian, will be down-coded to plain o. Chinese characters cannot be
813        ///   represented in codepage IBM437; when using the default encoding, Chinese
814        ///   characters in filenames will be represented as ?. These are all examples
815        ///   of "information loss".
816        /// </para>
817        ///
818        /// <para>
819        ///   The loss of information associated to the use of the IBM437 encoding is
820        ///   inconvenient, and can also lead to runtime errors. For example, using
821        ///   IBM437, any sequence of 4 Chinese characters will be encoded as ????.  If
822        ///   your application creates a <c>ZipOutputStream</c>, does not set the
823        ///   encoding, then adds two files, each with names of four Chinese characters
824        ///   each, this will result in a duplicate filename exception.  In the case
825        ///   where you add a single file with a name containing four Chinese
826        ///   characters, the zipfile will save properly, but extracting that file
827        ///   later, with any zip tool, will result in an error, because the question
828        ///   mark is not legal for use within filenames on Windows.  These are just a
829        ///   few examples of the problems associated to loss of information.
830        /// </para>
831        ///
832        /// <para>
833        ///   This flag is independent of the encoding of the content within the entries
834        ///   in the zip file. Think of the zip file as a container - it supports an
835        ///   encoding.  Within the container are other "containers" - the file entries
836        ///   themselves.  The encoding within those entries is independent of the
837        ///   encoding of the zip archive container for those entries.
838        /// </para>
839        ///
840        /// <para>
841        ///   Rather than specify the encoding in a binary fashion using this flag, an
842        ///   application can specify an arbitrary encoding via the <see
843        ///   cref="ProvisionalAlternateEncoding"/> property.  Setting the encoding
844        ///   explicitly when creating zip archives will result in non-compliant zip
845        ///   files that, curiously, are fairly interoperable.  The challenge is, the
846        ///   PKWare specification does not provide for a way to specify that an entry
847        ///   in a zip archive uses a code page that is neither IBM437 nor UTF-8.
848        ///   Therefore if you set the encoding explicitly when creating a zip archive,
849        ///   you must take care upon reading the zip archive to use the same code page.
850        ///   If you get it wrong, the behavior is undefined and may result in incorrect
851        ///   filenames, exceptions, stomach upset, hair loss, and acne.
852        /// </para>
853        /// </remarks>
854        /// <seealso cref="ProvisionalAlternateEncoding"/>
855        [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.")]
856        public bool UseUnicodeAsNecessary
857        {
858            get
859            {
860                return (_alternateEncoding == System.Text.Encoding.UTF8) &&
861                    (AlternateEncodingUsage == ZipOption.AsNecessary);
862            }
863            set
864            {
865                if (value)
866                {
867                    _alternateEncoding = System.Text.Encoding.UTF8;
868                    _alternateEncodingUsage = ZipOption.AsNecessary;
869
870                }
871                else
872                {
873                    _alternateEncoding = Ionic.Zip.ZipOutputStream.DefaultEncoding;
874                    _alternateEncodingUsage = ZipOption.Never;
875                }
876            }
877        }
878
879
880        /// <summary>
881        ///   The text encoding to use when emitting entries into the zip archive, for
882        ///   those entries whose filenames or comments cannot be encoded with the
883        ///   default (IBM437) encoding.
884        /// </summary>
885        ///
886        /// <remarks>
887        /// <para>
888        ///   In <see href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">its
889        ///   zip specification</see>, PKWare describes two options for encoding
890        ///   filenames and comments: using IBM437 or UTF-8.  But, some archiving tools
891        ///   or libraries do not follow the specification, and instead encode
892        ///   characters using the system default code page.  For example, WinRAR when
893        ///   run on a machine in Shanghai may encode filenames with the Big-5 Chinese
894        ///   (950) code page.  This behavior is contrary to the Zip specification, but
895        ///   it occurs anyway.
896        /// </para>
897        ///
898        /// <para>
899        ///   When using DotNetZip to write zip archives that will be read by one of
900        ///   these other archivers, set this property to specify the code page to use
901        ///   when encoding the <see cref="ZipEntry.FileName"/> and <see
902        ///   cref="ZipEntry.Comment"/> for each <c>ZipEntry</c> in the zip file, for
903        ///   values that cannot be encoded with the default codepage for zip files,
904        ///   IBM437.  This is why this property is "provisional".  In all cases, IBM437
905        ///   is used where possible, in other words, where no loss of data would
906        ///   result. It is possible, therefore, to have a given entry with a
907        ///   <c>Comment</c> encoded in IBM437 and a <c>FileName</c> encoded with the
908        ///   specified "provisional" codepage.
909        /// </para>
910        ///
911        /// <para>
912        ///   Be aware that a zip file created after you've explicitly set the
913        ///   <c>ProvisionalAlternateEncoding</c> property to a value other than
914        ///   IBM437 may not be compliant to the PKWare specification, and may not be
915        ///   readable by compliant archivers.  On the other hand, many (most?)
916        ///   archivers are non-compliant and can read zip files created in arbitrary
917        ///   code pages.  The trick is to use or specify the proper codepage when
918        ///   reading the zip.
919        /// </para>
920        ///
921        /// <para>
922        ///   When creating a zip archive using this library, it is possible to change
923        ///   the value of <c>ProvisionalAlternateEncoding</c> between each entry you
924        ///   add, and between adding entries and the call to <c>Close()</c>. Don't do
925        ///   this. It will likely result in a zipfile that is not readable.  For best
926        ///   interoperability, either leave <c>ProvisionalAlternateEncoding</c>
927        ///   alone, or specify it only once, before adding any entries to the
928        ///   <c>ZipOutputStream</c> instance.  There is one exception to this
929        ///   recommendation, described later.
930        /// </para>
931        ///
932        /// <para>
933        ///   When using an arbitrary, non-UTF8 code page for encoding, there is no
934        ///   standard way for the creator application - whether DotNetZip, WinZip,
935        ///   WinRar, or something else - to formally specify in the zip file which
936        ///   codepage has been used for the entries. As a result, readers of zip files
937        ///   are not able to inspect the zip file and determine the codepage that was
938        ///   used for the entries contained within it.  It is left to the application
939        ///   or user to determine the necessary codepage when reading zip files encoded
940        ///   this way.  If you use an incorrect codepage when reading a zipfile, you
941        ///   will get entries with filenames that are incorrect, and the incorrect
942        ///   filenames may even contain characters that are not legal for use within
943        ///   filenames in Windows. Extracting entries with illegal characters in the
944        ///   filenames will lead to exceptions. It's too bad, but this is just the way
945        ///   things are with code pages in zip files. Caveat Emptor.
946        /// </para>
947        ///
948        /// <para>
949        ///   One possible approach for specifying the code page for a given zip file is
950        ///   to describe the code page in a human-readable form in the Zip comment. For
951        ///   example, the comment may read "Entries in this archive are encoded in the
952        ///   Big5 code page".  For maximum interoperability, the zip comment in this
953        ///   case should be encoded in the default, IBM437 code page.  In this case,
954        ///   the zip comment is encoded using a different page than the filenames.  To
955        ///   do this, Specify <c>ProvisionalAlternateEncoding</c> to your desired
956        ///   region-specific code page, once before adding any entries, and then set
957        ///   the <see cref="Comment"/> property and reset
958        ///   <c>ProvisionalAlternateEncoding</c> to IBM437 before calling <c>Close()</c>.
959        /// </para>
960        /// </remarks>
961        [Obsolete("use AlternateEncoding and AlternateEncodingUsage instead.")]
962        public System.Text.Encoding ProvisionalAlternateEncoding
963        {
964            get
965            {
966                if (_alternateEncodingUsage == ZipOption.AsNecessary)
967                    return _alternateEncoding;
968                return null;
969            }
970            set
971            {
972                _alternateEncoding = value;
973                _alternateEncodingUsage = ZipOption.AsNecessary;
974            }
975        }
976
977        /// <summary>
978        ///   A Text Encoding to use when encoding the filenames and comments for
979        ///   all the ZipEntry items, during a ZipFile.Save() operation.
980        /// </summary>
981        /// <remarks>
982        ///   <para>
983        ///     Whether the encoding specified here is used during the save depends
984        ///     on <see cref="AlternateEncodingUsage"/>.
985        ///   </para>
986        /// </remarks>
987        public System.Text.Encoding AlternateEncoding
988        {
989            get
990            {
991                return _alternateEncoding;
992            }
993            set
994            {
995                _alternateEncoding = value;
996            }
997        }
998
999        /// <summary>
1000        ///   A flag that tells if and when this instance should apply
1001        ///   AlternateEncoding to encode the filenames and comments associated to
1002        ///   of ZipEntry objects contained within this instance.
1003        /// </summary>
1004        public ZipOption AlternateEncodingUsage
1005        {
1006            get
1007            {
1008                return _alternateEncodingUsage;
1009            }
1010            set
1011            {
1012                _alternateEncodingUsage = value;
1013            }
1014        }
1015
1016        /// <summary>
1017        /// The default text encoding used in zip archives.  It is numeric 437, also
1018        /// known as IBM437.
1019        /// </summary>
1020        /// <seealso cref="Ionic.Zip.ZipFile.ProvisionalAlternateEncoding"/>
1021        public static System.Text.Encoding DefaultEncoding
1022        {
1023            get
1024            {
1025                return System.Text.Encoding.GetEncoding("IBM437");
1026            }
1027        }
1028
1029
1030#if !NETCF
1031        /// <summary>
1032        ///   The size threshold for an entry, above which a parallel deflate is used.
1033        /// </summary>
1034        ///
1035        /// <remarks>
1036        ///
1037        ///   <para>
1038        ///     DotNetZip will use multiple threads to compress any ZipEntry, when
1039        ///     the <c>CompressionMethod</c> is Deflate, and if the entry is
1040        ///     larger than the given size.  Zero means "always use parallel
1041        ///     deflate", while -1 means "never use parallel deflate".
1042        ///   </para>
1043        ///
1044        ///   <para>
1045        ///     If the entry size cannot be known before compression, as with any entry
1046        ///     added via a ZipOutputStream, then Parallel deflate will never be
1047        ///     performed, unless the value of this property is zero.
1048        ///   </para>
1049        ///
1050        ///   <para>
1051        ///     A parallel deflate operations will speed up the compression of
1052        ///     large files, on computers with multiple CPUs or multiple CPU
1053        ///     cores.  For files above 1mb, on a dual core or dual-cpu (2p)
1054        ///     machine, the time required to compress the file can be 70% of the
1055        ///     single-threaded deflate.  For very large files on 4p machines the
1056        ///     compression can be done in 30% of the normal time.  The downside
1057        ///     is that parallel deflate consumes extra memory during the deflate,
1058        ///     and the deflation is slightly less effective.
1059        ///   </para>
1060        ///
1061        ///   <para>
1062        ///     Parallel deflate tends to not be as effective as single-threaded deflate
1063        ///     because the original data stream is split into multiple independent
1064        ///     buffers, each of which is compressed in parallel.  But because they are
1065        ///     treated independently, there is no opportunity to share compression
1066        ///     dictionaries, and additional framing bytes must be added to the output
1067        ///     stream.  For that reason, a deflated stream may be slightly larger when
1068        ///     compressed using parallel deflate, as compared to a traditional
1069        ///     single-threaded deflate. For files of about 512k, the increase over the
1070        ///     normal deflate is as much as 5% of the total compressed size. For larger
1071        ///     files, the difference can be as small as 0.1%.
1072        ///   </para>
1073        ///
1074        ///   <para>
1075        ///     Multi-threaded compression does not give as much an advantage when using
1076        ///     Encryption. This is primarily because encryption tends to slow down
1077        ///     the entire pipeline. Also, multi-threaded compression gives less of an
1078        ///     advantage when using lower compression levels, for example <see
1079        ///     cref="Ionic.Zlib.CompressionLevel.BestSpeed"/>.  You may have to perform
1080        ///     some tests to determine the best approach for your situation.
1081        ///   </para>
1082        ///
1083        ///   <para>
1084        ///     The default value for this property is -1, which means parallel
1085        ///     compression will not be performed unless you set it to zero.
1086        ///   </para>
1087        ///
1088        /// </remarks>
1089        public long ParallelDeflateThreshold
1090        {
1091            set
1092            {
1093                if ((value != 0) && (value != -1) && (value < 64 * 1024))
1094                    throw new ArgumentOutOfRangeException("value must be greater than 64k, or 0, or -1");
1095                _ParallelDeflateThreshold = value;
1096            }
1097            get
1098            {
1099                return _ParallelDeflateThreshold;
1100            }
1101        }
1102
1103
1104        /// <summary>
1105        ///   The maximum number of buffer pairs to use when performing
1106        ///   parallel compression.
1107        /// </summary>
1108        ///
1109        /// <remarks>
1110        /// <para>
1111        ///   This property sets an upper limit on the number of memory
1112        ///   buffer pairs to create when performing parallel
1113        ///   compression.  The implementation of the parallel
1114        ///   compression stream allocates multiple buffers to
1115        ///   facilitate parallel compression.  As each buffer fills up,
1116        ///   the stream uses <see
1117        ///   cref="System.Threading.ThreadPool.QueueUserWorkItem(WaitCallback)">
1118        ///   ThreadPool.QueueUserWorkItem()</see> to compress those
1119        ///   buffers in a background threadpool thread. After a buffer
1120        ///   is compressed, it is re-ordered and written to the output
1121        ///   stream.
1122        /// </para>
1123        ///
1124        /// <para>
1125        ///   A higher number of buffer pairs enables a higher degree of
1126        ///   parallelism, which tends to increase the speed of compression on
1127        ///   multi-cpu computers.  On the other hand, a higher number of buffer
1128        ///   pairs also implies a larger memory consumption, more active worker
1129        ///   threads, and a higher cpu utilization for any compression. This
1130        ///   property enables the application to limit its memory consumption and
1131        ///   CPU utilization behavior depending on requirements.
1132        /// </para>
1133        ///
1134        /// <para>
1135        ///   For each compression "task" that occurs in parallel, there are 2
1136        ///   buffers allocated: one for input and one for output.  This property
1137        ///   sets a limit for the number of pairs.  The total amount of storage
1138        ///   space allocated for buffering will then be (N*S*2), where N is the
1139        ///   number of buffer pairs, S is the size of each buffer (<see
1140        ///   cref="CodecBufferSize"/>).  By default, DotNetZip allocates 4 buffer
1141        ///   pairs per CPU core, so if your machine has 4 cores, and you retain
1142        ///   the default buffer size of 128k, then the
1143        ///   ParallelDeflateOutputStream will use 4 * 4 * 2 * 128kb of buffer
1144        ///   memory in total, or 4mb, in blocks of 128kb.  If you then set this
1145        ///   property to 8, then the number will be 8 * 2 * 128kb of buffer
1146        ///   memory, or 2mb.
1147        /// </para>
1148        ///
1149        /// <para>
1150        ///   CPU utilization will also go up with additional buffers, because a
1151        ///   larger number of buffer pairs allows a larger number of background
1152        ///   threads to compress in parallel. If you find that parallel
1153        ///   compression is consuming too much memory or CPU, you can adjust this
1154        ///   value downward.
1155        /// </para>
1156        ///
1157        /// <para>
1158        ///   The default value is 16. Different values may deliver better or
1159        ///   worse results, depending on your priorities and the dynamic
1160        ///   performance characteristics of your storage and compute resources.
1161        /// </para>
1162        ///
1163        /// <para>
1164        ///   This property is not the number of buffer pairs to use; it is an
1165        ///   upper limit. An illustration: Suppose you have an application that
1166        ///   uses the default value of this property (which is 16), and it runs
1167        ///   on a machine with 2 CPU cores. In that case, DotNetZip will allocate
1168        ///   4 buffer pairs per CPU core, for a total of 8 pairs.  The upper
1169        ///   limit specified by this property has no effect.
1170        /// </para>
1171        ///
1172        /// <para>
1173        ///   The application can set this value at any time, but it is
1174        ///   effective only if set before calling
1175        ///   <c>ZipOutputStream.Write()</c> for the first time.
1176        /// </para>
1177        /// </remarks>
1178        ///
1179        /// <seealso cref="ParallelDeflateThreshold"/>
1180        ///
1181        public int ParallelDeflateMaxBufferPairs
1182        {
1183            get
1184            {
1185                return _maxBufferPairs;
1186            }
1187            set
1188            {
1189                if (value < 4)
1190                    throw new ArgumentOutOfRangeException("ParallelDeflateMaxBufferPairs",
1191                                                "Value must be 4 or greater.");
1192                _maxBufferPairs = value;
1193            }
1194        }
1195#endif
1196
1197
1198        private void InsureUniqueEntry(ZipEntry ze1)
1199        {
1200            if (_entriesWritten.ContainsKey(ze1.FileName))
1201            {
1202                _exceptionPending = true;
1203                throw new ArgumentException(String.Format("The entry '{0}' already exists in the zip archive.", ze1.FileName));
1204            }
1205        }
1206
1207
1208        internal Stream OutputStream
1209        {
1210            get
1211            {
1212                return _outputStream;
1213            }
1214        }
1215
1216        internal String Name
1217        {
1218            get
1219            {
1220                return _name;
1221            }
1222        }
1223
1224        /// <summary>
1225        ///   Returns true if an entry by the given name has already been written
1226        ///   to the ZipOutputStream.
1227        /// </summary>
1228        ///
1229        /// <param name="name">
1230        ///   The name of the entry to scan for.
1231        /// </param>
1232        ///
1233        /// <returns>
1234        /// true if an entry by the given name has already been written.
1235        /// </returns>
1236        public bool ContainsEntry(string name)
1237        {
1238            return _entriesWritten.ContainsKey(SharedUtilities.NormalizePathForUseInZipFile(name));
1239        }
1240
1241
1242        /// <summary>
1243        ///   Write the data from the buffer to the stream.
1244        /// </summary>
1245        ///
1246        /// <remarks>
1247        ///   As the application writes data into this stream, the data may be
1248        ///   compressed and encrypted before being written out to the underlying
1249        ///   stream, depending on the settings of the <see cref="CompressionLevel"/>
1250        ///   and the <see cref="Encryption"/> properties.
1251        /// </remarks>
1252        ///
1253        /// <param name="buffer">The buffer holding data to write to the stream.</param>
1254        /// <param name="offset">the offset within that data array to find the first byte to write.</param>
1255        /// <param name="count">the number of bytes to write.</param>
1256        public override void Write(byte[] buffer, int offset, int count)
1257        {
1258            if (_disposed)
1259            {
1260                _exceptionPending = true;
1261                throw new System.InvalidOperationException("The stream has been closed.");
1262            }
1263
1264            if (buffer==null)
1265            {
1266                _exceptionPending = true;
1267                throw new System.ArgumentNullException("buffer");
1268            }
1269
1270            if (_currentEntry == null)
1271            {
1272                _exceptionPending = true;
1273                throw new System.InvalidOperationException("You must call PutNextEntry() before calling Write().");
1274            }
1275
1276            if (_currentEntry.IsDirectory)
1277            {
1278                _exceptionPending = true;
1279                throw new System.InvalidOperationException("You cannot Write() data for an entry that is a directory.");
1280            }
1281
1282            if (_needToWriteEntryHeader)
1283                _InitiateCurrentEntry(false);
1284
1285            if (count != 0)
1286                _entryOutputStream.Write(buffer, offset, count);
1287        }
1288
1289
1290
1291        /// <summary>
1292        ///   Specify the name of the next entry that will be written to the zip file.
1293        /// </summary>
1294        ///
1295        /// <remarks>
1296        /// <para>
1297        ///   Call this method just before calling <see cref="Write(byte[], int, int)"/>, to
1298        ///   specify the name of the entry that the next set of bytes written to
1299        ///   the <c>ZipOutputStream</c> belongs to. All subsequent calls to <c>Write</c>,
1300        ///   until the next call to <c>PutNextEntry</c>,
1301        ///   will be inserted into the named entry in the zip file.
1302        /// </para>
1303        ///
1304        /// <para>
1305        ///   If the <paramref name="entryName"/> used in <c>PutNextEntry()</c> ends in
1306        ///   a slash, then the entry added is marked as a directory. Because directory
1307        ///   entries do not contain data, a call to <c>Write()</c>, before an
1308        ///   intervening additional call to <c>PutNextEntry()</c>, will throw an
1309        ///   exception.
1310        /// </para>
1311        ///
1312        /// <para>
1313        ///   If you don't call <c>Write()</c> between two calls to
1314        ///   <c>PutNextEntry()</c>, the first entry is inserted into the zip file as a
1315        ///   file of zero size.  This may be what you want.
1316        /// </para>
1317        ///
1318        /// <para>
1319        ///   Because <c>PutNextEntry()</c> closes out the prior entry, if any, this
1320        ///   method may throw if there is a problem with the prior entry.
1321        /// </para>
1322        ///
1323        /// <para>
1324        ///   This method returns the <c>ZipEntry</c>.  You can modify public properties
1325        ///   on the <c>ZipEntry</c>, such as <see cref="ZipEntry.Encryption"/>, <see
1326        ///   cref="ZipEntry.Password"/>, and so on, until the first call to
1327        ///   <c>ZipOutputStream.Write()</c>, or until the next call to
1328        ///   <c>PutNextEntry()</c>.  If you modify the <c>ZipEntry</c> <em>after</em>
1329        ///   having called <c>Write()</c>, you may get a runtime exception, or you may
1330        ///   silently get an invalid zip archive.
1331        /// </para>
1332        ///
1333        /// </remarks>
1334        ///
1335        /// <example>
1336        ///
1337        ///   This example shows how to create a zip file, using the
1338        ///   <c>ZipOutputStream</c> class.
1339        ///
1340        /// <code>
1341        /// private void Zipup()
1342        /// {
1343        ///     using (FileStream fs raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite ))
1344        ///     {
1345        ///         using (var output= new ZipOutputStream(fs))
1346        ///         {
1347        ///             output.Password = "VerySecret!";
1348        ///             output.Encryption = EncryptionAlgorithm.WinZipAes256;
1349        ///             output.PutNextEntry("entry1.txt");
1350        ///             byte[] buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #1.");
1351        ///             output.Write(buffer,0,buffer.Length);
1352        ///             output.PutNextEntry("entry2.txt");  // this will be zero length
1353        ///             output.PutNextEntry("entry3.txt");
1354        ///             buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #3.");
1355        ///             output.Write(buffer,0,buffer.Length);
1356        ///         }
1357        ///     }
1358        /// }
1359        /// </code>
1360        /// </example>
1361        ///
1362        /// <param name="entryName">
1363        ///   The name of the entry to be added, including any path to be used
1364        ///   within the zip file.
1365        /// </param>
1366        ///
1367        /// <returns>
1368        ///   The ZipEntry created.
1369        /// </returns>
1370        ///
1371        public ZipEntry PutNextEntry(String entryName)
1372        {
1373            if (String.IsNullOrEmpty(entryName))
1374                throw new ArgumentNullException("entryName");
1375
1376            if (_disposed)
1377            {
1378                _exceptionPending = true;
1379                throw new System.InvalidOperationException("The stream has been closed.");
1380            }
1381
1382            _FinishCurrentEntry();
1383            _currentEntry = ZipEntry.CreateForZipOutputStream(entryName);
1384            _currentEntry._container = new ZipContainer(this);
1385            _currentEntry._BitField |= 0x0008;  // workitem 8932
1386            _currentEntry.SetEntryTimes(DateTime.Now, DateTime.Now, DateTime.Now);
1387            _currentEntry.CompressionLevel = this.CompressionLevel;
1388            _currentEntry.CompressionMethod = this.CompressionMethod;
1389            _currentEntry.Password = _password; // workitem 13909
1390            _currentEntry.Encryption = this.Encryption;
1391            // workitem 12634
1392            _currentEntry.AlternateEncoding = this.AlternateEncoding;
1393            _currentEntry.AlternateEncodingUsage = this.AlternateEncodingUsage;
1394
1395            if (entryName.EndsWith("/"))  _currentEntry.MarkAsDirectory();
1396
1397            _currentEntry.EmitTimesInWindowsFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Windows) != 0);
1398            _currentEntry.EmitTimesInUnixFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Unix) != 0);
1399            InsureUniqueEntry(_currentEntry);
1400            _needToWriteEntryHeader = true;
1401
1402            return _currentEntry;
1403        }
1404
1405
1406
1407        private void _InitiateCurrentEntry(bool finishing)
1408        {
1409            // If finishing==true, this means we're initiating the entry at the time of
1410            // Close() or PutNextEntry().  If this happens, it means no data was written
1411            // for the entry - Write() was never called.  (The usual case us to call
1412            // _InitiateCurrentEntry(bool) from within Write().)  If finishing==true,
1413            // the entry could be either a zero-byte file or a directory.
1414
1415            _entriesWritten.Add(_currentEntry.FileName,_currentEntry);
1416            _entryCount++; // could use _entriesWritten.Count, but I don't want to incur
1417            // the cost.
1418
1419            if (_entryCount > 65534 && _zip64 == Zip64Option.Never)
1420            {
1421                _exceptionPending = true;
1422                throw new System.InvalidOperationException("Too many entries. Consider setting ZipOutputStream.EnableZip64.");
1423            }
1424
1425            // Write out the header.
1426            //
1427            // If finishing, and encryption is in use, then we don't want to emit the
1428            // normal encryption header.  Signal that with a cycle=99 to turn off
1429            // encryption for zero-byte entries or directories.
1430            //
1431            // If finishing, then we know the stream length is zero.  Else, unknown
1432            // stream length.  Passing stream length == 0 allows an optimization so as
1433            // not to setup an encryption or deflation stream, when stream length is
1434            // zero.
1435
1436            _currentEntry.WriteHeader(_outputStream, finishing ? 99 : 0);
1437            _currentEntry.StoreRelativeOffset();
1438
1439            if (!_currentEntry.IsDirectory)
1440            {
1441                _currentEntry.WriteSecurityMetadata(_outputStream);
1442                _currentEntry.PrepOutputStream(_outputStream,
1443                                               finishing ? 0 : -1,
1444                                               out _outputCounter,
1445                                               out _encryptor,
1446                                               out _deflater,
1447                                               out _entryOutputStream);
1448            }
1449            _needToWriteEntryHeader = false;
1450        }
1451
1452
1453
1454        private void _FinishCurrentEntry()
1455        {
1456            if (_currentEntry != null)
1457            {
1458                if (_needToWriteEntryHeader)
1459                    _InitiateCurrentEntry(true); // an empty entry - no writes
1460
1461                _currentEntry.FinishOutputStream(_outputStream, _outputCounter, _encryptor, _deflater, _entryOutputStream);
1462                _currentEntry.PostProcessOutput(_outputStream);
1463                // workitem 12964
1464                if (_currentEntry.OutputUsedZip64!=null)
1465                    _anyEntriesUsedZip64 |= _currentEntry.OutputUsedZip64.Value;
1466
1467                // reset all the streams
1468                _outputCounter = null; _encryptor = _deflater = null; _entryOutputStream = null;
1469            }
1470        }
1471
1472
1473
1474        /// <summary>
1475        /// Dispose the stream
1476        /// </summary>
1477        ///
1478        /// <remarks>
1479        /// <para>
1480        ///   This method writes the Zip Central directory, then closes the stream.  The
1481        ///   application must call Dispose() (or Close) in order to produce a valid zip file.
1482        /// </para>
1483        ///
1484        /// <para>
1485        ///   Typically the application will call <c>Dispose()</c> implicitly, via a <c>using</c>
1486        ///   statement in C#, or a <c>Using</c> statement in VB.
1487        /// </para>
1488        ///
1489        /// </remarks>
1490        ///
1491        /// <param name="disposing">set this to true, always.</param>
1492        protected override void Dispose(bool disposing)
1493        {
1494            if (_disposed) return;
1495
1496            if (disposing) // not called from finalizer
1497            {
1498                // handle pending exceptions
1499                if (!_exceptionPending)
1500                {
1501                    _FinishCurrentEntry();
1502                    _directoryNeededZip64 = ZipOutput.WriteCentralDirectoryStructure(_outputStream,
1503                                                                                     _entriesWritten.Values,
1504                                                                                     1, // _numberOfSegmentsForMostRecentSave,
1505                                                                                     _zip64,
1506                                                                                     Comment,
1507                                                                                     new ZipContainer(this));
1508                    Stream wrappedStream = null;
1509                    CountingStream cs = _outputStream as CountingStream;
1510                    if (cs != null)
1511                    {
1512                        wrappedStream = cs.WrappedStream;
1513#if NETCF
1514                    cs.Close();
1515#else
1516                        cs.Dispose();
1517#endif
1518                    }
1519                    else
1520                    {
1521                        wrappedStream = _outputStream;
1522                    }
1523
1524                    if (!_leaveUnderlyingStreamOpen)
1525                    {
1526#if NETCF
1527                    wrappedStream.Close();
1528#else
1529                        wrappedStream.Dispose();
1530#endif
1531                    }
1532                    _outputStream = null;
1533                }
1534            }
1535            _disposed = true;
1536        }
1537
1538
1539
1540        /// <summary>
1541        /// Always returns false.
1542        /// </summary>
1543        public override bool CanRead { get { return false; } }
1544
1545        /// <summary>
1546        /// Always returns false.
1547        /// </summary>
1548        public override bool CanSeek { get { return false; } }
1549
1550        /// <summary>
1551        /// Always returns true.
1552        /// </summary>
1553        public override bool CanWrite { get { return true; } }
1554
1555        /// <summary>
1556        /// Always returns a NotSupportedException.
1557        /// </summary>
1558        public override long Length { get { throw new NotSupportedException(); } }
1559
1560        /// <summary>
1561        /// Setting this property always returns a NotSupportedException. Getting it
1562        /// returns the value of the Position on the underlying stream.
1563        /// </summary>
1564        public override long Position
1565        {
1566            get { return _outputStream.Position; }
1567            set { throw new NotSupportedException(); }
1568        }
1569
1570        /// <summary>
1571        /// This is a no-op.
1572        /// </summary>
1573        public override void Flush() { }
1574
1575        /// <summary>
1576        /// This method always throws a NotSupportedException.
1577        /// </summary>
1578        /// <param name="buffer">ignored</param>
1579        /// <param name="offset">ignored</param>
1580        /// <param name="count">ignored</param>
1581        /// <returns>nothing</returns>
1582        public override int Read(byte[] buffer, int offset, int count)
1583        {
1584            throw new NotSupportedException("Read");
1585        }
1586
1587        /// <summary>
1588        /// This method always throws a NotSupportedException.
1589        /// </summary>
1590        /// <param name="offset">ignored</param>
1591        /// <param name="origin">ignored</param>
1592        /// <returns>nothing</returns>
1593        public override long Seek(long offset, SeekOrigin origin)
1594        {
1595            throw new NotSupportedException("Seek");
1596        }
1597
1598        /// <summary>
1599        /// This method always throws a NotSupportedException.
1600        /// </summary>
1601        /// <param name="value">ignored</param>
1602        public override void SetLength(long value)
1603        {
1604            throw new NotSupportedException();
1605        }
1606
1607
1608        private EncryptionAlgorithm _encryption;
1609        private ZipEntryTimestamp _timestamp;
1610        internal String _password;
1611        private String _comment;
1612        private Stream _outputStream;
1613        private ZipEntry _currentEntry;
1614        internal Zip64Option _zip64;
1615        private Dictionary<String, ZipEntry> _entriesWritten;
1616        private int _entryCount;
1617        private ZipOption _alternateEncodingUsage = ZipOption.Never;
1618        private System.Text.Encoding _alternateEncoding
1619            = System.Text.Encoding.GetEncoding("IBM437"); // default = IBM437
1620
1621        private bool _leaveUnderlyingStreamOpen;
1622        private bool _disposed;
1623        private bool _exceptionPending; // **see note below
1624        private bool _anyEntriesUsedZip64, _directoryNeededZip64;
1625        private CountingStream _outputCounter;
1626        private Stream _encryptor;
1627        private Stream _deflater;
1628        private Ionic.Crc.CrcCalculatorStream _entryOutputStream;
1629        private bool _needToWriteEntryHeader;
1630        private string _name;
1631        private bool _DontIgnoreCase;
1632#if !NETCF
1633        internal ParallelDeflateOutputStream ParallelDeflater;
1634        private long _ParallelDeflateThreshold;
1635        private int _maxBufferPairs = 16;
1636#endif
1637
1638        // **Note regarding exceptions:
1639
1640        // When ZipOutputStream is employed within a using clause, which
1641        // is the typical scenario, and an exception is thrown within
1642        // the scope of the using, Close()/Dispose() is invoked
1643        // implicitly before processing the initial exception.  In that
1644        // case, _exceptionPending is true, and we don't want to try to
1645        // write anything in the Close/Dispose logic.  Doing so can
1646        // cause additional exceptions that mask the original one. So,
1647        // the _exceptionPending flag is used to track that, and to
1648        // allow the original exception to be propagated to the
1649        // application without extra "noise."
1650
1651    }
1652
1653
1654
1655    internal class ZipContainer
1656    {
1657        private ZipFile _zf;
1658        private ZipOutputStream _zos;
1659        private ZipInputStream _zis;
1660
1661        public ZipContainer(Object o)
1662        {
1663            _zf = (o as ZipFile);
1664            _zos = (o as ZipOutputStream);
1665            _zis = (o as ZipInputStream);
1666        }
1667
1668        public ZipFile ZipFile
1669        {
1670            get { return _zf; }
1671        }
1672
1673        public ZipOutputStream ZipOutputStream
1674        {
1675            get { return _zos; }
1676        }
1677
1678        public string Name
1679        {
1680            get
1681            {
1682                if (_zf != null) return _zf.Name;
1683                if (_zis != null) throw new NotSupportedException();
1684                return _zos.Name;
1685            }
1686        }
1687
1688        public string Password
1689        {
1690            get
1691            {
1692                if (_zf != null) return _zf._Password;
1693                if (_zis != null) return _zis._Password;
1694                return _zos._password;
1695            }
1696        }
1697
1698        public Zip64Option Zip64
1699        {
1700            get
1701            {
1702                if (_zf != null) return _zf._zip64;
1703                if (_zis != null) throw new NotSupportedException();
1704                return _zos._zip64;
1705            }
1706        }
1707
1708        public int BufferSize
1709        {
1710            get
1711            {
1712                if (_zf != null) return _zf.BufferSize;
1713                if (_zis != null) throw new NotSupportedException();
1714                return 0;
1715            }
1716        }
1717
1718#if !NETCF
1719        public Ionic.Zlib.ParallelDeflateOutputStream ParallelDeflater
1720        {
1721            get
1722            {
1723                if (_zf != null) return _zf.ParallelDeflater;
1724                if (_zis != null) return null;
1725                return _zos.ParallelDeflater;
1726            }
1727            set
1728            {
1729                if (_zf != null) _zf.ParallelDeflater = value;
1730                else if (_zos != null) _zos.ParallelDeflater = value;
1731            }
1732        }
1733
1734        public long ParallelDeflateThreshold
1735        {
1736            get
1737            {
1738                if (_zf != null) return _zf.ParallelDeflateThreshold;
1739                return _zos.ParallelDeflateThreshold;
1740            }
1741        }
1742        public int ParallelDeflateMaxBufferPairs
1743        {
1744            get
1745            {
1746                if (_zf != null) return _zf.ParallelDeflateMaxBufferPairs;
1747                return _zos.ParallelDeflateMaxBufferPairs;
1748            }
1749        }
1750#endif
1751
1752        public int CodecBufferSize
1753        {
1754            get
1755            {
1756                if (_zf != null) return _zf.CodecBufferSize;
1757                if (_zis != null) return _zis.CodecBufferSize;
1758                return _zos.CodecBufferSize;
1759            }
1760        }
1761
1762        public Ionic.Zlib.CompressionStrategy Strategy
1763        {
1764            get
1765            {
1766                if (_zf != null) return _zf.Strategy;
1767                return _zos.Strategy;
1768            }
1769        }
1770
1771        public Zip64Option UseZip64WhenSaving
1772        {
1773            get
1774            {
1775                if (_zf != null) return _zf.UseZip64WhenSaving;
1776                return _zos.EnableZip64;
1777            }
1778        }
1779
1780        public System.Text.Encoding AlternateEncoding
1781        {
1782            get
1783            {
1784                if (_zf != null) return _zf.AlternateEncoding;
1785                if (_zos!=null) return _zos.AlternateEncoding;
1786                return null;
1787            }
1788        }
1789        public System.Text.Encoding DefaultEncoding
1790        {
1791            get
1792            {
1793                if (_zf != null) return ZipFile.DefaultEncoding;
1794                if (_zos!=null) return ZipOutputStream.DefaultEncoding;
1795                return null;
1796            }
1797        }
1798        public ZipOption AlternateEncodingUsage
1799        {
1800            get
1801            {
1802                if (_zf != null) return _zf.AlternateEncodingUsage;
1803                if (_zos!=null) return _zos.AlternateEncodingUsage;
1804                return ZipOption.Never; // n/a
1805            }
1806        }
1807
1808        public Stream ReadStream
1809        {
1810            get
1811            {
1812                if (_zf != null) return _zf.ReadStream;
1813                return _zis.ReadStream;
1814            }
1815        }
1816    }
1817
1818}
Note: See TracBrowser for help on using the repository browser.