FastZip.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. using ICSharpCode.SharpZipLib.Core;
  2. using ICSharpCode.SharpZipLib.Zip.Compression;
  3. using System;
  4. using System.IO;
  5. using static ICSharpCode.SharpZipLib.Zip.Compression.Deflater;
  6. namespace ICSharpCode.SharpZipLib.Zip
  7. {
  8. /// <summary>
  9. /// FastZipEvents supports all events applicable to <see cref="FastZip">FastZip</see> operations.
  10. /// </summary>
  11. public class FastZipEvents
  12. {
  13. /// <summary>
  14. /// Delegate to invoke when processing directories.
  15. /// </summary>
  16. public event EventHandler<DirectoryEventArgs> ProcessDirectory;
  17. /// <summary>
  18. /// Delegate to invoke when processing files.
  19. /// </summary>
  20. public ProcessFileHandler ProcessFile;
  21. /// <summary>
  22. /// Delegate to invoke during processing of files.
  23. /// </summary>
  24. public ProgressHandler Progress;
  25. /// <summary>
  26. /// Delegate to invoke when processing for a file has been completed.
  27. /// </summary>
  28. public CompletedFileHandler CompletedFile;
  29. /// <summary>
  30. /// Delegate to invoke when processing directory failures.
  31. /// </summary>
  32. public DirectoryFailureHandler DirectoryFailure;
  33. /// <summary>
  34. /// Delegate to invoke when processing file failures.
  35. /// </summary>
  36. public FileFailureHandler FileFailure;
  37. /// <summary>
  38. /// Raise the <see cref="DirectoryFailure">directory failure</see> event.
  39. /// </summary>
  40. /// <param name="directory">The directory causing the failure.</param>
  41. /// <param name="e">The exception for this event.</param>
  42. /// <returns>A boolean indicating if execution should continue or not.</returns>
  43. public bool OnDirectoryFailure(string directory, Exception e)
  44. {
  45. bool result = false;
  46. DirectoryFailureHandler handler = DirectoryFailure;
  47. if (handler != null)
  48. {
  49. var args = new ScanFailureEventArgs(directory, e);
  50. handler(this, args);
  51. result = args.ContinueRunning;
  52. }
  53. return result;
  54. }
  55. /// <summary>
  56. /// Fires the <see cref="FileFailure"> file failure handler delegate</see>.
  57. /// </summary>
  58. /// <param name="file">The file causing the failure.</param>
  59. /// <param name="e">The exception for this failure.</param>
  60. /// <returns>A boolean indicating if execution should continue or not.</returns>
  61. public bool OnFileFailure(string file, Exception e)
  62. {
  63. FileFailureHandler handler = FileFailure;
  64. bool result = (handler != null);
  65. if (result)
  66. {
  67. var args = new ScanFailureEventArgs(file, e);
  68. handler(this, args);
  69. result = args.ContinueRunning;
  70. }
  71. return result;
  72. }
  73. /// <summary>
  74. /// Fires the <see cref="ProcessFile">ProcessFile delegate</see>.
  75. /// </summary>
  76. /// <param name="file">The file being processed.</param>
  77. /// <returns>A boolean indicating if execution should continue or not.</returns>
  78. public bool OnProcessFile(string file)
  79. {
  80. bool result = true;
  81. ProcessFileHandler handler = ProcessFile;
  82. if (handler != null)
  83. {
  84. var args = new ScanEventArgs(file);
  85. handler(this, args);
  86. result = args.ContinueRunning;
  87. }
  88. return result;
  89. }
  90. /// <summary>
  91. /// Fires the <see cref="CompletedFile"/> delegate
  92. /// </summary>
  93. /// <param name="file">The file whose processing has been completed.</param>
  94. /// <returns>A boolean indicating if execution should continue or not.</returns>
  95. public bool OnCompletedFile(string file)
  96. {
  97. bool result = true;
  98. CompletedFileHandler handler = CompletedFile;
  99. if (handler != null)
  100. {
  101. var args = new ScanEventArgs(file);
  102. handler(this, args);
  103. result = args.ContinueRunning;
  104. }
  105. return result;
  106. }
  107. /// <summary>
  108. /// Fires the <see cref="ProcessDirectory">process directory</see> delegate.
  109. /// </summary>
  110. /// <param name="directory">The directory being processed.</param>
  111. /// <param name="hasMatchingFiles">Flag indicating if the directory has matching files as determined by the current filter.</param>
  112. /// <returns>A <see cref="bool"/> of true if the operation should continue; false otherwise.</returns>
  113. public bool OnProcessDirectory(string directory, bool hasMatchingFiles)
  114. {
  115. bool result = true;
  116. EventHandler<DirectoryEventArgs> handler = ProcessDirectory;
  117. if (handler != null)
  118. {
  119. var args = new DirectoryEventArgs(directory, hasMatchingFiles);
  120. handler(this, args);
  121. result = args.ContinueRunning;
  122. }
  123. return result;
  124. }
  125. /// <summary>
  126. /// The minimum timespan between <see cref="Progress"/> events.
  127. /// </summary>
  128. /// <value>The minimum period of time between <see cref="Progress"/> events.</value>
  129. /// <seealso cref="Progress"/>
  130. /// <remarks>The default interval is three seconds.</remarks>
  131. public TimeSpan ProgressInterval
  132. {
  133. get { return progressInterval_; }
  134. set { progressInterval_ = value; }
  135. }
  136. #region Instance Fields
  137. private TimeSpan progressInterval_ = TimeSpan.FromSeconds(3);
  138. #endregion Instance Fields
  139. }
  140. /// <summary>
  141. /// FastZip provides facilities for creating and extracting zip files.
  142. /// </summary>
  143. public class FastZip
  144. {
  145. #region Enumerations
  146. /// <summary>
  147. /// Defines the desired handling when overwriting files during extraction.
  148. /// </summary>
  149. public enum Overwrite
  150. {
  151. /// <summary>
  152. /// Prompt the user to confirm overwriting
  153. /// </summary>
  154. Prompt,
  155. /// <summary>
  156. /// Never overwrite files.
  157. /// </summary>
  158. Never,
  159. /// <summary>
  160. /// Always overwrite files.
  161. /// </summary>
  162. Always
  163. }
  164. #endregion Enumerations
  165. #region Constructors
  166. /// <summary>
  167. /// Initialise a default instance of <see cref="FastZip"/>.
  168. /// </summary>
  169. public FastZip()
  170. {
  171. }
  172. /// <summary>
  173. /// Initialise a new instance of <see cref="FastZip"/>
  174. /// </summary>
  175. /// <param name="events">The <see cref="FastZipEvents">events</see> to use during operations.</param>
  176. public FastZip(FastZipEvents events)
  177. {
  178. events_ = events;
  179. }
  180. #endregion Constructors
  181. #region Properties
  182. /// <summary>
  183. /// Get/set a value indicating wether empty directories should be created.
  184. /// </summary>
  185. public bool CreateEmptyDirectories
  186. {
  187. get { return createEmptyDirectories_; }
  188. set { createEmptyDirectories_ = value; }
  189. }
  190. /// <summary>
  191. /// Get / set the password value.
  192. /// </summary>
  193. public string Password
  194. {
  195. get { return password_; }
  196. set { password_ = value; }
  197. }
  198. /// <summary>
  199. /// Get or set the <see cref="INameTransform"></see> active when creating Zip files.
  200. /// </summary>
  201. /// <seealso cref="EntryFactory"></seealso>
  202. public INameTransform NameTransform
  203. {
  204. get { return entryFactory_.NameTransform; }
  205. set
  206. {
  207. entryFactory_.NameTransform = value;
  208. }
  209. }
  210. /// <summary>
  211. /// Get or set the <see cref="IEntryFactory"></see> active when creating Zip files.
  212. /// </summary>
  213. public IEntryFactory EntryFactory
  214. {
  215. get { return entryFactory_; }
  216. set
  217. {
  218. if (value == null)
  219. {
  220. entryFactory_ = new ZipEntryFactory();
  221. }
  222. else
  223. {
  224. entryFactory_ = value;
  225. }
  226. }
  227. }
  228. /// <summary>
  229. /// Gets or sets the setting for <see cref="UseZip64">Zip64 handling when writing.</see>
  230. /// </summary>
  231. /// <remarks>
  232. /// The default value is dynamic which is not backwards compatible with old
  233. /// programs and can cause problems with XP's built in compression which cant
  234. /// read Zip64 archives. However it does avoid the situation were a large file
  235. /// is added and cannot be completed correctly.
  236. /// NOTE: Setting the size for entries before they are added is the best solution!
  237. /// By default the EntryFactory used by FastZip will set fhe file size.
  238. /// </remarks>
  239. public UseZip64 UseZip64
  240. {
  241. get { return useZip64_; }
  242. set { useZip64_ = value; }
  243. }
  244. /// <summary>
  245. /// Get/set a value indicating wether file dates and times should
  246. /// be restored when extracting files from an archive.
  247. /// </summary>
  248. /// <remarks>The default value is false.</remarks>
  249. public bool RestoreDateTimeOnExtract
  250. {
  251. get
  252. {
  253. return restoreDateTimeOnExtract_;
  254. }
  255. set
  256. {
  257. restoreDateTimeOnExtract_ = value;
  258. }
  259. }
  260. /// <summary>
  261. /// Get/set a value indicating whether file attributes should
  262. /// be restored during extract operations
  263. /// </summary>
  264. public bool RestoreAttributesOnExtract
  265. {
  266. get { return restoreAttributesOnExtract_; }
  267. set { restoreAttributesOnExtract_ = value; }
  268. }
  269. /// <summary>
  270. /// Get/set the Compression Level that will be used
  271. /// when creating the zip
  272. /// </summary>
  273. public Deflater.CompressionLevel CompressionLevel
  274. {
  275. get { return compressionLevel_; }
  276. set { compressionLevel_ = value; }
  277. }
  278. #endregion Properties
  279. #region Delegates
  280. /// <summary>
  281. /// Delegate called when confirming overwriting of files.
  282. /// </summary>
  283. public delegate bool ConfirmOverwriteDelegate(string fileName);
  284. #endregion Delegates
  285. #region CreateZip
  286. /// <summary>
  287. /// Create a zip file.
  288. /// </summary>
  289. /// <param name="zipFileName">The name of the zip file to create.</param>
  290. /// <param name="sourceDirectory">The directory to source files from.</param>
  291. /// <param name="recurse">True to recurse directories, false for no recursion.</param>
  292. /// <param name="fileFilter">The <see cref="PathFilter">file filter</see> to apply.</param>
  293. /// <param name="directoryFilter">The <see cref="PathFilter">directory filter</see> to apply.</param>
  294. public void CreateZip(string zipFileName, string sourceDirectory,
  295. bool recurse, string fileFilter, string directoryFilter)
  296. {
  297. CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, directoryFilter);
  298. }
  299. /// <summary>
  300. /// Create a zip file/archive.
  301. /// </summary>
  302. /// <param name="zipFileName">The name of the zip file to create.</param>
  303. /// <param name="sourceDirectory">The directory to obtain files and directories from.</param>
  304. /// <param name="recurse">True to recurse directories, false for no recursion.</param>
  305. /// <param name="fileFilter">The file filter to apply.</param>
  306. public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter)
  307. {
  308. CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, null);
  309. }
  310. /// <summary>
  311. /// Create a zip archive sending output to the <paramref name="outputStream"/> passed.
  312. /// </summary>
  313. /// <param name="outputStream">The stream to write archive data to.</param>
  314. /// <param name="sourceDirectory">The directory to source files from.</param>
  315. /// <param name="recurse">True to recurse directories, false for no recursion.</param>
  316. /// <param name="fileFilter">The <see cref="PathFilter">file filter</see> to apply.</param>
  317. /// <param name="directoryFilter">The <see cref="PathFilter">directory filter</see> to apply.</param>
  318. /// <remarks>The <paramref name="outputStream"/> is closed after creation.</remarks>
  319. public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter)
  320. {
  321. NameTransform = new ZipNameTransform(sourceDirectory);
  322. sourceDirectory_ = sourceDirectory;
  323. using (outputStream_ = new ZipOutputStream(outputStream))
  324. {
  325. outputStream_.SetLevel((int)CompressionLevel);
  326. if (password_ != null)
  327. {
  328. outputStream_.Password = password_;
  329. }
  330. outputStream_.UseZip64 = UseZip64;
  331. var scanner = new FileSystemScanner(fileFilter, directoryFilter);
  332. scanner.ProcessFile += ProcessFile;
  333. if (this.CreateEmptyDirectories)
  334. {
  335. scanner.ProcessDirectory += ProcessDirectory;
  336. }
  337. if (events_ != null)
  338. {
  339. if (events_.FileFailure != null)
  340. {
  341. scanner.FileFailure += events_.FileFailure;
  342. }
  343. if (events_.DirectoryFailure != null)
  344. {
  345. scanner.DirectoryFailure += events_.DirectoryFailure;
  346. }
  347. }
  348. scanner.Scan(sourceDirectory, recurse);
  349. }
  350. }
  351. #endregion CreateZip
  352. #region ExtractZip
  353. /// <summary>
  354. /// Extract the contents of a zip file.
  355. /// </summary>
  356. /// <param name="zipFileName">The zip file to extract from.</param>
  357. /// <param name="targetDirectory">The directory to save extracted information in.</param>
  358. /// <param name="fileFilter">A filter to apply to files.</param>
  359. public void ExtractZip(string zipFileName, string targetDirectory, string fileFilter)
  360. {
  361. ExtractZip(zipFileName, targetDirectory, Overwrite.Always, null, fileFilter, null, restoreDateTimeOnExtract_);
  362. }
  363. /// <summary>
  364. /// Extract the contents of a zip file.
  365. /// </summary>
  366. /// <param name="zipFileName">The zip file to extract from.</param>
  367. /// <param name="targetDirectory">The directory to save extracted information in.</param>
  368. /// <param name="overwrite">The style of <see cref="Overwrite">overwriting</see> to apply.</param>
  369. /// <param name="confirmDelegate">A delegate to invoke when confirming overwriting.</param>
  370. /// <param name="fileFilter">A filter to apply to files.</param>
  371. /// <param name="directoryFilter">A filter to apply to directories.</param>
  372. /// <param name="restoreDateTime">Flag indicating whether to restore the date and time for extracted files.</param>
  373. /// <param name="allowParentTraversal">Allow parent directory traversal in file paths (e.g. ../file)</param>
  374. public void ExtractZip(string zipFileName, string targetDirectory,
  375. Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate,
  376. string fileFilter, string directoryFilter, bool restoreDateTime, bool allowParentTraversal = false)
  377. {
  378. Stream inputStream = File.Open(zipFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
  379. ExtractZip(inputStream, targetDirectory, overwrite, confirmDelegate, fileFilter, directoryFilter, restoreDateTime, true, allowParentTraversal);
  380. }
  381. /// <summary>
  382. /// Extract the contents of a zip file held in a stream.
  383. /// </summary>
  384. /// <param name="inputStream">The seekable input stream containing the zip to extract from.</param>
  385. /// <param name="targetDirectory">The directory to save extracted information in.</param>
  386. /// <param name="overwrite">The style of <see cref="Overwrite">overwriting</see> to apply.</param>
  387. /// <param name="confirmDelegate">A delegate to invoke when confirming overwriting.</param>
  388. /// <param name="fileFilter">A filter to apply to files.</param>
  389. /// <param name="directoryFilter">A filter to apply to directories.</param>
  390. /// <param name="restoreDateTime">Flag indicating whether to restore the date and time for extracted files.</param>
  391. /// <param name="isStreamOwner">Flag indicating whether the inputStream will be closed by this method.</param>
  392. /// <param name="allowParentTraversal">Allow parent directory traversal in file paths (e.g. ../file)</param>
  393. public void ExtractZip(Stream inputStream, string targetDirectory,
  394. Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate,
  395. string fileFilter, string directoryFilter, bool restoreDateTime,
  396. bool isStreamOwner, bool allowParentTraversal = false)
  397. {
  398. if ((overwrite == Overwrite.Prompt) && (confirmDelegate == null))
  399. {
  400. throw new ArgumentNullException(nameof(confirmDelegate));
  401. }
  402. continueRunning_ = true;
  403. overwrite_ = overwrite;
  404. confirmDelegate_ = confirmDelegate;
  405. extractNameTransform_ = new WindowsNameTransform(targetDirectory, allowParentTraversal);
  406. fileFilter_ = new NameFilter(fileFilter);
  407. directoryFilter_ = new NameFilter(directoryFilter);
  408. restoreDateTimeOnExtract_ = restoreDateTime;
  409. using (zipFile_ = new ZipFile(inputStream, !isStreamOwner))
  410. {
  411. if (password_ != null)
  412. {
  413. zipFile_.Password = password_;
  414. }
  415. System.Collections.IEnumerator enumerator = zipFile_.GetEnumerator();
  416. while (continueRunning_ && enumerator.MoveNext())
  417. {
  418. var entry = (ZipEntry)enumerator.Current;
  419. if (entry.IsFile)
  420. {
  421. // TODO Path.GetDirectory can fail here on invalid characters.
  422. if (directoryFilter_.IsMatch(Path.GetDirectoryName(entry.Name)) && fileFilter_.IsMatch(entry.Name))
  423. {
  424. ExtractEntry(entry);
  425. }
  426. }
  427. else if (entry.IsDirectory)
  428. {
  429. if (directoryFilter_.IsMatch(entry.Name) && CreateEmptyDirectories)
  430. {
  431. ExtractEntry(entry);
  432. }
  433. }
  434. else
  435. {
  436. // Do nothing for volume labels etc...
  437. }
  438. }
  439. }
  440. }
  441. #endregion ExtractZip
  442. #region Internal Processing
  443. private void ProcessDirectory(object sender, DirectoryEventArgs e)
  444. {
  445. if (!e.HasMatchingFiles && CreateEmptyDirectories)
  446. {
  447. if (events_ != null)
  448. {
  449. events_.OnProcessDirectory(e.Name, e.HasMatchingFiles);
  450. }
  451. if (e.ContinueRunning)
  452. {
  453. if (e.Name != sourceDirectory_)
  454. {
  455. ZipEntry entry = entryFactory_.MakeDirectoryEntry(e.Name);
  456. outputStream_.PutNextEntry(entry);
  457. }
  458. }
  459. }
  460. }
  461. private void ProcessFile(object sender, ScanEventArgs e)
  462. {
  463. if ((events_ != null) && (events_.ProcessFile != null))
  464. {
  465. events_.ProcessFile(sender, e);
  466. }
  467. if (e.ContinueRunning)
  468. {
  469. try
  470. {
  471. // The open below is equivalent to OpenRead which gaurantees that if opened the
  472. // file will not be changed by subsequent openers, but precludes opening in some cases
  473. // were it could succeed. ie the open may fail as its already open for writing and the share mode should reflect that.
  474. using (FileStream stream = File.Open(e.Name, FileMode.Open, FileAccess.Read, FileShare.Read))
  475. {
  476. ZipEntry entry = entryFactory_.MakeFileEntry(e.Name);
  477. outputStream_.PutNextEntry(entry);
  478. AddFileContents(e.Name, stream);
  479. }
  480. }
  481. catch (Exception ex)
  482. {
  483. if (events_ != null)
  484. {
  485. continueRunning_ = events_.OnFileFailure(e.Name, ex);
  486. }
  487. else
  488. {
  489. continueRunning_ = false;
  490. throw;
  491. }
  492. }
  493. }
  494. }
  495. private void AddFileContents(string name, Stream stream)
  496. {
  497. if (stream == null)
  498. {
  499. throw new ArgumentNullException(nameof(stream));
  500. }
  501. if (buffer_ == null)
  502. {
  503. buffer_ = new byte[4096];
  504. }
  505. if ((events_ != null) && (events_.Progress != null))
  506. {
  507. StreamUtils.Copy(stream, outputStream_, buffer_,
  508. events_.Progress, events_.ProgressInterval, this, name);
  509. }
  510. else
  511. {
  512. StreamUtils.Copy(stream, outputStream_, buffer_);
  513. }
  514. if (events_ != null)
  515. {
  516. continueRunning_ = events_.OnCompletedFile(name);
  517. }
  518. }
  519. private void ExtractFileEntry(ZipEntry entry, string targetName)
  520. {
  521. bool proceed = true;
  522. if (overwrite_ != Overwrite.Always)
  523. {
  524. if (File.Exists(targetName))
  525. {
  526. if ((overwrite_ == Overwrite.Prompt) && (confirmDelegate_ != null))
  527. {
  528. proceed = confirmDelegate_(targetName);
  529. }
  530. else
  531. {
  532. proceed = false;
  533. }
  534. }
  535. }
  536. if (proceed)
  537. {
  538. if (events_ != null)
  539. {
  540. continueRunning_ = events_.OnProcessFile(entry.Name);
  541. }
  542. if (continueRunning_)
  543. {
  544. try
  545. {
  546. using (FileStream outputStream = File.Create(targetName))
  547. {
  548. if (buffer_ == null)
  549. {
  550. buffer_ = new byte[4096];
  551. }
  552. if ((events_ != null) && (events_.Progress != null))
  553. {
  554. StreamUtils.Copy(zipFile_.GetInputStream(entry), outputStream, buffer_,
  555. events_.Progress, events_.ProgressInterval, this, entry.Name, entry.Size);
  556. }
  557. else
  558. {
  559. StreamUtils.Copy(zipFile_.GetInputStream(entry), outputStream, buffer_);
  560. }
  561. if (events_ != null)
  562. {
  563. continueRunning_ = events_.OnCompletedFile(entry.Name);
  564. }
  565. }
  566. if (restoreDateTimeOnExtract_)
  567. {
  568. File.SetLastWriteTime(targetName, entry.DateTime);
  569. }
  570. if (RestoreAttributesOnExtract && entry.IsDOSEntry && (entry.ExternalFileAttributes != -1))
  571. {
  572. var fileAttributes = (FileAttributes)entry.ExternalFileAttributes;
  573. // TODO: FastZip - Setting of other file attributes on extraction is a little trickier.
  574. fileAttributes &= (FileAttributes.Archive | FileAttributes.Normal | FileAttributes.ReadOnly | FileAttributes.Hidden);
  575. File.SetAttributes(targetName, fileAttributes);
  576. }
  577. }
  578. catch (Exception ex)
  579. {
  580. if (events_ != null)
  581. {
  582. continueRunning_ = events_.OnFileFailure(targetName, ex);
  583. }
  584. else
  585. {
  586. continueRunning_ = false;
  587. throw;
  588. }
  589. }
  590. }
  591. }
  592. }
  593. private void ExtractEntry(ZipEntry entry)
  594. {
  595. bool doExtraction = entry.IsCompressionMethodSupported();
  596. string targetName = entry.Name;
  597. if (doExtraction)
  598. {
  599. if (entry.IsFile)
  600. {
  601. targetName = extractNameTransform_.TransformFile(targetName);
  602. }
  603. else if (entry.IsDirectory)
  604. {
  605. targetName = extractNameTransform_.TransformDirectory(targetName);
  606. }
  607. doExtraction = !(string.IsNullOrEmpty(targetName));
  608. }
  609. // TODO: Fire delegate/throw exception were compression method not supported, or name is invalid?
  610. string dirName = null;
  611. if (doExtraction)
  612. {
  613. if (entry.IsDirectory)
  614. {
  615. dirName = targetName;
  616. }
  617. else
  618. {
  619. dirName = Path.GetDirectoryName(Path.GetFullPath(targetName));
  620. }
  621. }
  622. if (doExtraction && !Directory.Exists(dirName))
  623. {
  624. if (!entry.IsDirectory || CreateEmptyDirectories)
  625. {
  626. try
  627. {
  628. Directory.CreateDirectory(dirName);
  629. }
  630. catch (Exception ex)
  631. {
  632. doExtraction = false;
  633. if (events_ != null)
  634. {
  635. if (entry.IsDirectory)
  636. {
  637. continueRunning_ = events_.OnDirectoryFailure(targetName, ex);
  638. }
  639. else
  640. {
  641. continueRunning_ = events_.OnFileFailure(targetName, ex);
  642. }
  643. }
  644. else
  645. {
  646. continueRunning_ = false;
  647. throw;
  648. }
  649. }
  650. }
  651. }
  652. if (doExtraction && entry.IsFile)
  653. {
  654. ExtractFileEntry(entry, targetName);
  655. }
  656. }
  657. private static int MakeExternalAttributes(FileInfo info)
  658. {
  659. return (int)info.Attributes;
  660. }
  661. private static bool NameIsValid(string name)
  662. {
  663. return !string.IsNullOrEmpty(name) &&
  664. (name.IndexOfAny(Path.GetInvalidPathChars()) < 0);
  665. }
  666. #endregion Internal Processing
  667. #region Instance Fields
  668. private bool continueRunning_;
  669. private byte[] buffer_;
  670. private ZipOutputStream outputStream_;
  671. private ZipFile zipFile_;
  672. private string sourceDirectory_;
  673. private NameFilter fileFilter_;
  674. private NameFilter directoryFilter_;
  675. private Overwrite overwrite_;
  676. private ConfirmOverwriteDelegate confirmDelegate_;
  677. private bool restoreDateTimeOnExtract_;
  678. private bool restoreAttributesOnExtract_;
  679. private bool createEmptyDirectories_;
  680. private FastZipEvents events_;
  681. private IEntryFactory entryFactory_ = new ZipEntryFactory();
  682. private INameTransform extractNameTransform_;
  683. private UseZip64 useZip64_ = UseZip64.Dynamic;
  684. private CompressionLevel compressionLevel_ = CompressionLevel.DEFAULT_COMPRESSION;
  685. private string password_;
  686. #endregion Instance Fields
  687. }
  688. }