ZipFile.Extract.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // ZipFile.Extract.cs
  2. // ------------------------------------------------------------------
  3. //
  4. // Copyright (c) 2009 Dino Chiesa.
  5. // All rights reserved.
  6. //
  7. // This code module is part of DotNetZip, a zipfile class library.
  8. //
  9. // ------------------------------------------------------------------
  10. //
  11. // This code is licensed under the Microsoft Public License.
  12. // See the file License.txt for the license details.
  13. // More info on: http://dotnetzip.codeplex.com
  14. //
  15. // ------------------------------------------------------------------
  16. //
  17. // last saved (in emacs):
  18. // Time-stamp: <2011-July-31 14:45:18>
  19. //
  20. // ------------------------------------------------------------------
  21. //
  22. // This module defines the methods for Extract operations on zip files.
  23. //
  24. // ------------------------------------------------------------------
  25. //
  26. using System;
  27. using System.IO;
  28. using System.Collections.Generic;
  29. namespace Ionic.Zip
  30. {
  31. public partial class ZipFile
  32. {
  33. /// <summary>
  34. /// Extracts all of the items in the zip archive, to the specified path in the
  35. /// filesystem. The path can be relative or fully-qualified.
  36. /// </summary>
  37. ///
  38. /// <remarks>
  39. /// <para>
  40. /// This method will extract all entries in the <c>ZipFile</c> to the
  41. /// specified path.
  42. /// </para>
  43. ///
  44. /// <para>
  45. /// If an extraction of a file from the zip archive would overwrite an
  46. /// existing file in the filesystem, the action taken is dictated by the
  47. /// ExtractExistingFile property, which overrides any setting you may have
  48. /// made on individual ZipEntry instances. By default, if you have not
  49. /// set that property on the <c>ZipFile</c> instance, the entry will not
  50. /// be extracted, the existing file will not be overwritten and an
  51. /// exception will be thrown. To change this, set the property, or use the
  52. /// <see cref="ZipFile.ExtractAll(string,
  53. /// Ionic.Zip.ExtractExistingFileAction)" /> overload that allows you to
  54. /// specify an ExtractExistingFileAction parameter.
  55. /// </para>
  56. ///
  57. /// <para>
  58. /// The action to take when an extract would overwrite an existing file
  59. /// applies to all entries. If you want to set this on a per-entry basis,
  60. /// then you must use one of the <see
  61. /// cref="ZipEntry.Extract()">ZipEntry.Extract</see> methods.
  62. /// </para>
  63. ///
  64. /// <para>
  65. /// This method will send verbose output messages to the <see
  66. /// cref="StatusMessageTextWriter"/>, if it is set on the <c>ZipFile</c>
  67. /// instance.
  68. /// </para>
  69. ///
  70. /// <para>
  71. /// You may wish to take advantage of the <c>ExtractProgress</c> event.
  72. /// </para>
  73. ///
  74. /// <para>
  75. /// About timestamps: When extracting a file entry from a zip archive, the
  76. /// extracted file gets the last modified time of the entry as stored in
  77. /// the archive. The archive may also store extended file timestamp
  78. /// information, including last accessed and created times. If these are
  79. /// present in the <c>ZipEntry</c>, then the extracted file will also get
  80. /// these times.
  81. /// </para>
  82. ///
  83. /// <para>
  84. /// A Directory entry is somewhat different. It will get the times as
  85. /// described for a file entry, but, if there are file entries in the zip
  86. /// archive that, when extracted, appear in the just-created directory,
  87. /// then when those file entries are extracted, the last modified and last
  88. /// accessed times of the directory will change, as a side effect. The
  89. /// result is that after an extraction of a directory and a number of
  90. /// files within the directory, the last modified and last accessed
  91. /// timestamps on the directory will reflect the time that the last file
  92. /// was extracted into the directory, rather than the time stored in the
  93. /// zip archive for the directory.
  94. /// </para>
  95. ///
  96. /// <para>
  97. /// To compensate, when extracting an archive with <c>ExtractAll</c>,
  98. /// DotNetZip will extract all the file and directory entries as described
  99. /// above, but it will then make a second pass on the directories, and
  100. /// reset the times on the directories to reflect what is stored in the
  101. /// zip archive.
  102. /// </para>
  103. ///
  104. /// <para>
  105. /// This compensation is performed only within the context of an
  106. /// <c>ExtractAll</c>. If you call <c>ZipEntry.Extract</c> on a directory
  107. /// entry, the timestamps on directory in the filesystem will reflect the
  108. /// times stored in the zip. If you then call <c>ZipEntry.Extract</c> on
  109. /// a file entry, which is extracted into the directory, the timestamps on
  110. /// the directory will be updated to the current time.
  111. /// </para>
  112. /// </remarks>
  113. ///
  114. /// <example>
  115. /// This example extracts all the entries in a zip archive file, to the
  116. /// specified target directory. The extraction will overwrite any
  117. /// existing files silently.
  118. ///
  119. /// <code>
  120. /// String TargetDirectory= "unpack";
  121. /// using(ZipFile zip= ZipFile.Read(ZipFileToExtract))
  122. /// {
  123. /// zip.ExtractExistingFile= ExtractExistingFileAction.OverwriteSilently;
  124. /// zip.ExtractAll(TargetDirectory);
  125. /// }
  126. /// </code>
  127. ///
  128. /// <code lang="VB">
  129. /// Dim TargetDirectory As String = "unpack"
  130. /// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract)
  131. /// zip.ExtractExistingFile= ExtractExistingFileAction.OverwriteSilently
  132. /// zip.ExtractAll(TargetDirectory)
  133. /// End Using
  134. /// </code>
  135. /// </example>
  136. ///
  137. /// <seealso cref="Ionic.Zip.ZipFile.ExtractProgress"/>
  138. /// <seealso cref="Ionic.Zip.ZipFile.ExtractExistingFile"/>
  139. ///
  140. /// <param name="path">
  141. /// The path to which the contents of the zipfile will be extracted.
  142. /// The path can be relative or fully-qualified.
  143. /// </param>
  144. ///
  145. public void ExtractAll(string path)
  146. {
  147. _InternalExtractAll(path, true);
  148. }
  149. /// <summary>
  150. /// Extracts all of the items in the zip archive, to the specified path in the
  151. /// filesystem, using the specified behavior when extraction would overwrite an
  152. /// existing file.
  153. /// </summary>
  154. ///
  155. /// <remarks>
  156. ///
  157. /// <para>
  158. /// This method will extract all entries in the <c>ZipFile</c> to the specified
  159. /// path. For an extraction that would overwrite an existing file, the behavior
  160. /// is dictated by <paramref name="extractExistingFile"/>, which overrides any
  161. /// setting you may have made on individual ZipEntry instances.
  162. /// </para>
  163. ///
  164. /// <para>
  165. /// The action to take when an extract would overwrite an existing file
  166. /// applies to all entries. If you want to set this on a per-entry basis,
  167. /// then you must use <see cref="ZipEntry.Extract(String,
  168. /// ExtractExistingFileAction)" /> or one of the similar methods.
  169. /// </para>
  170. ///
  171. /// <para>
  172. /// Calling this method is equivalent to setting the <see
  173. /// cref="ExtractExistingFile"/> property and then calling <see
  174. /// cref="ExtractAll(String)"/>.
  175. /// </para>
  176. ///
  177. /// <para>
  178. /// This method will send verbose output messages to the
  179. /// <see cref="StatusMessageTextWriter"/>, if it is set on the <c>ZipFile</c> instance.
  180. /// </para>
  181. /// </remarks>
  182. ///
  183. /// <example>
  184. /// This example extracts all the entries in a zip archive file, to the
  185. /// specified target directory. It does not overwrite any existing files.
  186. /// <code>
  187. /// String TargetDirectory= "c:\\unpack";
  188. /// using(ZipFile zip= ZipFile.Read(ZipFileToExtract))
  189. /// {
  190. /// zip.ExtractAll(TargetDirectory, ExtractExistingFileAction.DontOverwrite);
  191. /// }
  192. /// </code>
  193. ///
  194. /// <code lang="VB">
  195. /// Dim TargetDirectory As String = "c:\unpack"
  196. /// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract)
  197. /// zip.ExtractAll(TargetDirectory, ExtractExistingFileAction.DontOverwrite)
  198. /// End Using
  199. /// </code>
  200. /// </example>
  201. ///
  202. /// <param name="path">
  203. /// The path to which the contents of the zipfile will be extracted.
  204. /// The path can be relative or fully-qualified.
  205. /// </param>
  206. ///
  207. /// <param name="extractExistingFile">
  208. /// The action to take if extraction would overwrite an existing file.
  209. /// </param>
  210. /// <seealso cref="ExtractSelectedEntries(String,ExtractExistingFileAction)"/>
  211. public void ExtractAll(string path, ExtractExistingFileAction extractExistingFile)
  212. {
  213. ExtractExistingFile = extractExistingFile;
  214. _InternalExtractAll(path, true);
  215. }
  216. private void _InternalExtractAll(string path, bool overrideExtractExistingProperty)
  217. {
  218. bool header = Verbose;
  219. _inExtractAll = true;
  220. try
  221. {
  222. OnExtractAllStarted(path);
  223. int n = 0;
  224. foreach (ZipEntry e in _entries.Values)
  225. {
  226. if (header)
  227. {
  228. StatusMessageTextWriter.WriteLine("\n{1,-22} {2,-8} {3,4} {4,-8} {0}",
  229. "Name", "Modified", "Size", "Ratio", "Packed");
  230. StatusMessageTextWriter.WriteLine(new System.String('-', 72));
  231. header = false;
  232. }
  233. if (Verbose)
  234. {
  235. StatusMessageTextWriter.WriteLine("{1,-22} {2,-8} {3,4:F0}% {4,-8} {0}",
  236. e.FileName,
  237. e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"),
  238. e.UncompressedSize,
  239. e.CompressionRatio,
  240. e.CompressedSize);
  241. if (!String.IsNullOrEmpty(e.Comment))
  242. StatusMessageTextWriter.WriteLine(" Comment: {0}", e.Comment);
  243. }
  244. e.Password = _Password; // this may be null
  245. OnExtractEntry(n, true, e, path);
  246. if (overrideExtractExistingProperty)
  247. e.ExtractExistingFile = this.ExtractExistingFile;
  248. e.Extract(path);
  249. n++;
  250. OnExtractEntry(n, false, e, path);
  251. if (_extractOperationCanceled)
  252. break;
  253. }
  254. if (!_extractOperationCanceled)
  255. {
  256. // workitem 8264:
  257. // now, set times on directory entries, again.
  258. // The problem is, extracting a file changes the times on the parent
  259. // directory. So after all files have been extracted, we have to
  260. // run through the directories again.
  261. foreach (ZipEntry e in _entries.Values)
  262. {
  263. // check if it is a directory
  264. if ((e.IsDirectory) || (e.FileName.EndsWith("/")))
  265. {
  266. string outputFile = (e.FileName.StartsWith("/"))
  267. ? Path.Combine(path, e.FileName.Substring(1))
  268. : Path.Combine(path, e.FileName);
  269. e._SetTimes(outputFile, false);
  270. }
  271. }
  272. OnExtractAllCompleted(path);
  273. }
  274. }
  275. finally
  276. {
  277. _inExtractAll = false;
  278. }
  279. }
  280. }
  281. }