ZipOutputStream.cs 73 KB

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