12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817 |
- // 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;
- namespace 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>
- public 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 = Ionic.Zlib.CompressionLevel.Default;
- CompressionMethod = 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="Ionic.Zip.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 Ionic.Zlib.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 Ionic.Zlib.CompressionLevel CompressionLevel
- {
- get;
- set;
- }
- /// <summary>
- /// The compression method used on each entry added to the ZipOutputStream.
- /// </summary>
- public Ionic.Zip.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 Ionic.Zlib.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;
- }
- }
- }
- }
|