PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/Utilities/Compression/Streams/ZipOutputStream.cs

#
C# | 829 lines | 507 code | 87 blank | 235 comment | 83 complexity | 37589ea4fd71f20ead507ba2e3e7d100 MD5 | raw file
Possible License(s): Apache-2.0
  1. // Based on Mike Krueger's SharpZipLib, Copyright (C) 2001 (GNU license).
  2. // Authors of the original java version: Jochen Hoenicke, John Leuner
  3. // See http://www.ISeeSharpCode.com for more information.
  4. using System;
  5. using System.Collections.Generic;
  6. using System.IO;
  7. using Delta.Utilities.Compression.Checksums;
  8. using Delta.Utilities.Compression.Deflaters;
  9. using Delta.Utilities.Helpers;
  10. namespace Delta.Utilities.Compression.Streams
  11. {
  12. #region Summary
  13. /// <summary>
  14. /// This is a DeflaterOutputStream that writes the files into a zip
  15. /// archive one after another. It has a special method to start a new
  16. /// zip entry. The zip entries contains information about the file name
  17. /// size, compressed size, CRC, etc.
  18. ///
  19. /// It includes support for Stored and Deflated entries.
  20. /// This class is not thread safe.
  21. /// </summary>
  22. /// <example>This sample shows how to create a zip file
  23. /// <code>
  24. /// using System;
  25. /// using System.IO;
  26. ///
  27. /// using Delta.Utilities.Compression;
  28. ///
  29. /// class MainClass
  30. /// {
  31. /// public static void Main(string[] args)
  32. /// {
  33. /// string[] filenames = Directory.GetFiles(args[0]);
  34. ///
  35. /// ZipOutputStream s = new ZipOutputStream(File.Create(args[1]));
  36. ///
  37. /// s.SetLevel(5); // 0 - store only to 9 - means best compression
  38. ///
  39. /// foreach (string file in filenames)
  40. /// {
  41. /// FileStream fs = File.OpenRead(file);
  42. ///
  43. /// byte[] buffer = new byte[fs.Length];
  44. /// fs.Read(buffer, 0, buffer.Length);
  45. ///
  46. /// ZipEntry entry = new ZipEntry(file);
  47. ///
  48. /// s.PutNextEntry(entry);
  49. ///
  50. /// s.Write(buffer, 0, buffer.Length);
  51. /// } // foreach
  52. ///
  53. /// s.Finish();
  54. /// s.Close();
  55. /// } // Main(args)
  56. /// } // class MainClass
  57. /// </code>
  58. /// </example>
  59. #endregion
  60. public class ZipOutputStream : DeflaterOutputStream
  61. {
  62. #region IsFinished (Public)
  63. /// <summary>
  64. /// Gets boolean indicating central header has been added for this
  65. /// archive... No further entries can be added once this has been done.
  66. /// </summary>
  67. public bool IsFinished
  68. {
  69. get
  70. {
  71. return entries == null;
  72. } // get
  73. }
  74. #endregion
  75. #region Private
  76. #region entries (Private)
  77. /// <summary>
  78. /// Entries
  79. /// </summary>
  80. private List<ZipEntry> entries = new List<ZipEntry>();
  81. #endregion
  82. #region crc (Private)
  83. /// <summary>
  84. /// Crc
  85. /// </summary>
  86. /// <returns>Crc 32</returns>
  87. private readonly Crc32 crc = new Crc32();
  88. #endregion
  89. #region curEntry (Private)
  90. /// <summary>
  91. /// Cur entry
  92. /// </summary>
  93. /// <returns>Null</returns>
  94. private ZipEntry curEntry;
  95. #endregion
  96. #region defaultCompressionLevel (Private)
  97. /// <summary>
  98. /// Default compression level
  99. /// </summary>
  100. /// <returns>Default compression</returns>
  101. private int defaultCompressionLevel;
  102. #endregion
  103. #region curMethod (Private)
  104. /// <summary>
  105. /// Cur method
  106. /// </summary>
  107. /// <returns>Deflated</returns>
  108. private CompressionMethod curMethod;
  109. #endregion
  110. #region size (Private)
  111. /// <summary>
  112. /// Size
  113. /// </summary>
  114. private long size;
  115. #endregion
  116. #region currentOffset (Private)
  117. /// <summary>
  118. /// Offset
  119. /// </summary>
  120. /// <returns>0</returns>
  121. private long currentOffset;
  122. #endregion
  123. #region zipComment (Private)
  124. /// <summary>
  125. /// Zip comment
  126. /// </summary>
  127. /// <returns>Byte</returns>
  128. private byte[] zipComment;
  129. #endregion
  130. #region patchEntryHeader (Private)
  131. /// <summary>
  132. /// Patch entry header
  133. /// </summary>
  134. /// <returns>False</returns>
  135. private bool patchEntryHeader;
  136. #endregion
  137. #region headerPatchPos (Private)
  138. /// <summary>
  139. /// Header patch pos
  140. /// </summary>
  141. /// <returns>-</returns>
  142. private long headerPatchPos;
  143. #endregion
  144. #endregion
  145. #region Constructors
  146. /// <summary>
  147. /// Creates a new Zip output stream, writing a zip archive.
  148. /// </summary>
  149. /// <param name="baseOutputStream">
  150. /// The output stream to which the archive contents are written.
  151. /// </param>
  152. internal ZipOutputStream(Stream baseOutputStream)
  153. : this(baseOutputStream, true)
  154. {
  155. }
  156. /// <summary>
  157. /// Creates a new Zip output stream, writing a zip archive.
  158. /// </summary>
  159. /// <param name="baseOutputStream">
  160. /// the output stream to which the zip archive is written.
  161. /// </param>
  162. /// <param name="nowrap">
  163. /// Specify this to skip the header and footer for the zip file.
  164. /// </param>
  165. internal ZipOutputStream(Stream baseOutputStream, bool nowrap)
  166. : base(baseOutputStream,
  167. new Deflater(Deflater.DefaultCompression, nowrap))
  168. {
  169. defaultCompressionLevel = Deflater.DefaultCompression;
  170. curMethod = CompressionMethod.Deflated;
  171. patchEntryHeader = false;
  172. headerPatchPos = MathHelper.InvalidIndex;
  173. zipComment = new byte[0];
  174. }
  175. #endregion
  176. #region SetComment (Public)
  177. /// <summary>
  178. /// Set the zip file comment.
  179. /// </summary>
  180. /// <param name="comment">
  181. /// The comment string
  182. /// </param>
  183. /// <exception name ="ArgumentOutOfRangeException">
  184. /// Encoding of comment is longer than 0xffff bytes.
  185. /// </exception>
  186. public void SetComment(string comment)
  187. {
  188. byte[] commentBytes = StringHelper.ToByteArray(comment);
  189. if (commentBytes.Length > 0xffff)
  190. {
  191. throw new ArgumentOutOfRangeException("comment");
  192. } // if
  193. zipComment = commentBytes;
  194. }
  195. #endregion
  196. #region SetCompressionLevel (Public)
  197. /// <summary>
  198. /// Sets default compression level (0=none - 9=best). The new level will be
  199. /// activated immediately.
  200. /// </summary>
  201. /// <exception cref="ArgumentOutOfRangeException">
  202. /// Level specified is not supported.
  203. /// </exception>
  204. /// <see cref="Deflater"/>
  205. public void SetCompressionLevel(int level)
  206. {
  207. defaultCompressionLevel = level;
  208. deflater.SetLevel(level);
  209. }
  210. #endregion
  211. #region GetLevel (Public)
  212. /// <summary>
  213. /// Get the current deflate compression level
  214. /// </summary>
  215. /// <returns>The current compression level</returns>
  216. public int GetLevel()
  217. {
  218. return deflater.GetLevel();
  219. }
  220. #endregion
  221. #region Write (Public)
  222. /// <summary>
  223. /// Writes the given buffer to the current entry.
  224. /// </summary>
  225. /// <exception cref="ZipException">
  226. /// Archive size is invalid
  227. /// </exception>
  228. /// <exception cref="System.InvalidOperationException">
  229. /// No entry is active.
  230. /// </exception>
  231. public override void Write(byte[] buffer, int offset, int count)
  232. {
  233. if (curEntry == null)
  234. {
  235. throw new InvalidOperationException("No open entry.");
  236. } // if
  237. if (count <= 0)
  238. {
  239. return;
  240. } // if
  241. crc.Update(buffer, offset, count);
  242. size += count;
  243. if (size > 0xffffffff ||
  244. size < 0)
  245. {
  246. throw new ZipException("Maximum entry size exceeded");
  247. } // if
  248. switch (curMethod)
  249. {
  250. case CompressionMethod.Deflated:
  251. base.Write(buffer, offset, count);
  252. break;
  253. case CompressionMethod.Stored:
  254. if (Password != null)
  255. {
  256. byte[] buf = new byte[count];
  257. Array.Copy(buffer, offset, buf, 0, count);
  258. EncryptBlock(buf, 0, count);
  259. baseOutputStream.Write(buf, offset, count);
  260. } // if
  261. else
  262. {
  263. baseOutputStream.Write(buffer, offset, count);
  264. } // else
  265. break;
  266. } // switch
  267. }
  268. #endregion
  269. #region Finish (Public)
  270. /// <summary>
  271. /// Finishes the stream. This will write the central directory at the
  272. /// end of the zip file and flush the stream.
  273. /// </summary>
  274. /// <remarks>
  275. /// This is automatically called when the stream is closed.
  276. /// </remarks>
  277. /// <exception cref="System.IO.IOException">
  278. /// An I/O error occurs.
  279. /// </exception>
  280. /// <exception cref="ZipException">
  281. /// Comment exceeds the maximum length<br/>
  282. /// Entry name exceeds the maximum length
  283. /// </exception>
  284. public override void Finish()
  285. {
  286. if (entries == null ||
  287. baseOutputStream == null ||
  288. baseOutputStream.CanWrite == false)
  289. {
  290. return;
  291. } // if
  292. if (curEntry != null)
  293. {
  294. CloseEntry();
  295. } // if
  296. int numEntries = 0;
  297. int sizeEntries = 0;
  298. foreach (ZipEntry entry in entries)
  299. {
  300. CompressionMethod method = entry.CompressionMethod;
  301. WriteLeInt(ZipConstants.CentralDirectorySig);
  302. WriteLeShort(ZipConstants.VersionMadeBy);
  303. WriteLeShort(entry.Version);
  304. WriteLeShort(entry.Flags);
  305. WriteLeShort((short)method);
  306. WriteLeInt((int)entry.DosTime);
  307. WriteLeInt((int)entry.Crc);
  308. WriteLeInt((int)entry.CompressedSize);
  309. WriteLeInt((int)entry.Size);
  310. byte[] name = StringHelper.ToByteArray(entry.Name);
  311. if (name.Length > 0xffff)
  312. {
  313. throw new ZipException("Name too long.");
  314. } // if
  315. byte[] extra = entry.GetExtraData();
  316. if (extra == null)
  317. {
  318. extra = new byte[0];
  319. } // if
  320. byte[] entryComment = (entry.Comment != null)
  321. ? StringHelper.ToByteArray(entry.Comment)
  322. : new byte[0];
  323. if (entryComment.Length > 0xffff)
  324. {
  325. throw new ZipException("Comment too long.");
  326. } // if
  327. WriteLeShort(name.Length);
  328. WriteLeShort(extra.Length);
  329. WriteLeShort(entryComment.Length);
  330. // disk number
  331. WriteLeShort(0);
  332. // internal file attributes
  333. WriteLeShort(0);
  334. // external file attribute
  335. if (entry.ExternalFileAttributes != MathHelper.InvalidIndex)
  336. {
  337. WriteLeInt(entry.ExternalFileAttributes);
  338. } // if
  339. else
  340. {
  341. // mark entry as directory (from nikolam.AT.perfectinfo.com)
  342. if (entry.IsDirectory)
  343. {
  344. WriteLeInt(16);
  345. } // if
  346. else
  347. {
  348. WriteLeInt(0);
  349. } // else
  350. } // else
  351. WriteLeInt(entry.Offset);
  352. baseOutputStream.Write(name, 0, name.Length);
  353. baseOutputStream.Write(extra, 0, extra.Length);
  354. baseOutputStream.Write(entryComment, 0, entryComment.Length);
  355. ++numEntries;
  356. sizeEntries += ZipConstants.CentralHeader + name.Length +
  357. extra.Length + entryComment.Length;
  358. } // foreach
  359. WriteLeInt(ZipConstants.EndSig);
  360. // number of this disk
  361. WriteLeShort(0);
  362. // no of disk with start of central dir
  363. WriteLeShort(0);
  364. // entries in central dir for this disk
  365. WriteLeShort(numEntries);
  366. // total entries in central directory
  367. WriteLeShort(numEntries);
  368. // size of the central directory
  369. WriteLeInt(sizeEntries);
  370. // offset of start of central dir
  371. WriteLeInt((int)currentOffset);
  372. WriteLeShort(zipComment.Length);
  373. baseOutputStream.Write(zipComment, 0, zipComment.Length);
  374. baseOutputStream.Flush();
  375. entries = null;
  376. }
  377. #endregion
  378. #region Methods (Private)
  379. #region WriteLeShort
  380. /// <summary>
  381. /// Write an unsigned short in little endian byte order.
  382. /// </summary>
  383. private void WriteLeShort(int value)
  384. {
  385. baseOutputStream.WriteByte((byte)(value & 0xff));
  386. baseOutputStream.WriteByte((byte)((value >> 8) & 0xff));
  387. }
  388. #endregion
  389. #region WriteLeInt
  390. /// <summary>
  391. /// Write an int in little endian byte order.
  392. /// </summary>
  393. private void WriteLeInt(int value)
  394. {
  395. WriteLeShort(value);
  396. WriteLeShort(value >> 16);
  397. }
  398. #endregion
  399. #region WriteLeLong
  400. /// <summary>
  401. /// Write an int in little endian byte order.
  402. /// </summary>
  403. private void WriteLeLong(long value)
  404. {
  405. WriteLeInt((int)value);
  406. WriteLeInt((int)(value >> 32));
  407. }
  408. #endregion
  409. #region PutNextEntry
  410. /// <summary>
  411. /// Starts a new Zip entry. It automatically closes the previous
  412. /// entry if present.
  413. /// All entry elements bar name are optional, but must be correct if present.
  414. /// If the compression method is stored and the output is not patchable
  415. /// the compression for that entry is automatically changed to deflate level 0
  416. /// </summary>
  417. /// <param name="entry">
  418. /// the entry.
  419. /// </param>
  420. /// <exception cref="System.IO.IOException">
  421. /// if an I/O error occured.
  422. /// </exception>
  423. /// <exception cref="System.InvalidOperationException">
  424. /// if stream was finished
  425. /// </exception>
  426. /// <exception cref="ZipException">
  427. /// Too many entries in the Zip file<br/>
  428. /// Entry name is too long<br/>
  429. /// Finish has already been called<br/>
  430. /// </exception>
  431. internal void PutNextEntry(ZipEntry entry)
  432. {
  433. if (entries == null)
  434. {
  435. throw new InvalidOperationException("ZipOutputStream was finished");
  436. } // if
  437. if (curEntry != null)
  438. {
  439. CloseEntry();
  440. } // if
  441. if (entries.Count >= 0xffff)
  442. {
  443. throw new ZipException("Too many entries for Zip file");
  444. } // if
  445. CompressionMethod method = entry.CompressionMethod;
  446. int compressionLevel = defaultCompressionLevel;
  447. entry.Flags = 0;
  448. patchEntryHeader = false;
  449. bool headerInfoAvailable = true;
  450. PrepareEntryHeader(entry, ref method, ref compressionLevel,
  451. ref headerInfoAvailable);
  452. curMethod = method;
  453. // Write the local file header
  454. WriteLeInt(ZipConstants.LocalSignature);
  455. WriteLeShort(entry.Version);
  456. WriteLeShort(entry.Flags);
  457. WriteLeShort((byte)method);
  458. WriteLeInt((int)entry.DosTime);
  459. if (headerInfoAvailable)
  460. {
  461. WriteLeInt((int)entry.Crc);
  462. WriteLeInt(entry.IsCrypted
  463. ? (int)entry.CompressedSize + ZipConstants.CryptoHeaderSize
  464. : (int)entry.CompressedSize);
  465. WriteLeInt((int)entry.Size);
  466. } // if
  467. else
  468. {
  469. if (patchEntryHeader)
  470. {
  471. headerPatchPos = baseOutputStream.Position;
  472. } // if
  473. // Crc
  474. WriteLeInt(0);
  475. // Compressed size
  476. WriteLeInt(0);
  477. // Uncompressed size
  478. WriteLeInt(0);
  479. } // else
  480. byte[] name = StringHelper.ToByteArray(entry.Name);
  481. if (name.Length > 0xFFFF)
  482. {
  483. throw new ZipException("Entry name too long.");
  484. } // if
  485. byte[] extra = entry.GetExtraData();
  486. if (extra == null)
  487. {
  488. extra = new byte[0];
  489. } // if
  490. if (extra.Length > 0xFFFF)
  491. {
  492. throw new ZipException("Extra data too long.");
  493. } // if
  494. WriteLeShort(name.Length);
  495. WriteLeShort(extra.Length);
  496. baseOutputStream.Write(name, 0, name.Length);
  497. baseOutputStream.Write(extra, 0, extra.Length);
  498. currentOffset += ZipConstants.LocalHeader + name.Length + extra.Length;
  499. // Activate the entry.
  500. curEntry = entry;
  501. crc.Reset();
  502. if (method == CompressionMethod.Deflated)
  503. {
  504. deflater.Reset();
  505. deflater.SetLevel(compressionLevel);
  506. } // if
  507. size = 0;
  508. if (entry.IsCrypted)
  509. {
  510. if (entry.Crc < 0)
  511. {
  512. // so testing Zip will says its ok
  513. WriteEncryptionHeader(entry.DosTime << 16);
  514. } // if
  515. else
  516. {
  517. WriteEncryptionHeader(entry.Crc);
  518. } // else
  519. } // if
  520. }
  521. #endregion
  522. #region PrepareEntryHeader
  523. /// <summary>
  524. /// Prepare entry header
  525. /// </summary>
  526. /// <param name="entry">Entry</param>
  527. /// <param name="method">Method</param>
  528. /// <param name="compressionLevel">Compression level</param>
  529. /// <param name="headerInfoAvailable">Header info available</param>
  530. private void PrepareEntryHeader(ZipEntry entry,
  531. ref CompressionMethod method, ref int compressionLevel,
  532. ref bool headerInfoAvailable)
  533. {
  534. if (method == CompressionMethod.Stored)
  535. {
  536. if (entry.CompressedSize >= 0)
  537. {
  538. if (entry.Size < 0)
  539. {
  540. entry.Size = entry.CompressedSize;
  541. } // if
  542. else if (entry.Size != entry.CompressedSize)
  543. {
  544. throw new ZipException(
  545. "Method Stored, but compressed size != size");
  546. } // else if
  547. } // if
  548. else
  549. {
  550. if (entry.Size >= 0)
  551. {
  552. entry.CompressedSize = entry.Size;
  553. } // if
  554. } // else
  555. if (entry.Size < 0 ||
  556. entry.Crc < 0)
  557. {
  558. if (CanPatchEntries)
  559. {
  560. headerInfoAvailable = false;
  561. } // if
  562. else
  563. {
  564. // Cant patch entries so storing is not possible.
  565. method = CompressionMethod.Deflated;
  566. compressionLevel = 0;
  567. } // else
  568. } // if
  569. } // if
  570. if (method == CompressionMethod.Deflated)
  571. {
  572. if (entry.Size == 0)
  573. {
  574. // No need to compress - no data.
  575. entry.CompressedSize = entry.Size;
  576. entry.Crc = 0;
  577. method = CompressionMethod.Stored;
  578. } // if
  579. else if (entry.CompressedSize < 0 ||
  580. entry.Size < 0 ||
  581. entry.Crc < 0)
  582. {
  583. headerInfoAvailable = false;
  584. } // else if
  585. } // if
  586. if (headerInfoAvailable == false)
  587. {
  588. if (CanPatchEntries == false)
  589. {
  590. entry.Flags |= 8;
  591. } // if
  592. else
  593. {
  594. patchEntryHeader = true;
  595. } // else
  596. } // if
  597. if (Password != null)
  598. {
  599. entry.IsCrypted = true;
  600. if (entry.Crc < 0)
  601. {
  602. // Need to append data descriptor as crc is used for encryption and
  603. // its not known.
  604. entry.Flags |= 8;
  605. } // if
  606. } // if
  607. entry.Offset = (int)currentOffset;
  608. entry.CompressionMethod = method;
  609. }
  610. #endregion
  611. #region CloseEntry
  612. /// <summary>
  613. /// Closes the current entry, updating header and footer information as
  614. /// required
  615. /// </summary>
  616. /// <exception cref="System.IO.IOException">
  617. /// An I/O error occurs.
  618. /// </exception>
  619. /// <exception cref="System.InvalidOperationException">
  620. /// No entry is active.
  621. /// </exception>
  622. internal void CloseEntry()
  623. {
  624. if (curEntry == null)
  625. {
  626. throw new InvalidOperationException("No open entry");
  627. } // if
  628. // First finish the deflater, if appropriate
  629. if (curMethod == CompressionMethod.Deflated)
  630. {
  631. base.Finish();
  632. } // if
  633. long csize = curMethod == CompressionMethod.Deflated
  634. ? deflater.TotalOut
  635. : size;
  636. if (curEntry.Size < 0)
  637. {
  638. curEntry.Size = size;
  639. } // if
  640. else if (curEntry.Size != size)
  641. {
  642. throw new ZipException("size was " + size + ", but I expected " +
  643. curEntry.Size);
  644. } // else if
  645. if (curEntry.CompressedSize < 0)
  646. {
  647. curEntry.CompressedSize = csize;
  648. } // if
  649. else if (curEntry.CompressedSize != csize)
  650. {
  651. throw new ZipException("compressed size was " + csize +
  652. ", but I expected " + curEntry.CompressedSize);
  653. } // else if
  654. if (curEntry.Crc < 0)
  655. {
  656. curEntry.Crc = crc.Value;
  657. } // if
  658. else if (curEntry.Crc != crc.Value)
  659. {
  660. throw new ZipException("crc was " + crc.Value + ", but I expected " +
  661. curEntry.Crc);
  662. } // else if
  663. currentOffset += csize;
  664. if (currentOffset > 0xffffffff)
  665. {
  666. throw new ZipException("Maximum Zip file size exceeded");
  667. } // if
  668. if (curEntry.IsCrypted)
  669. {
  670. curEntry.CompressedSize += ZipConstants.CryptoHeaderSize;
  671. } // if
  672. // Patch the header if possible
  673. if (patchEntryHeader)
  674. {
  675. long curPos = baseOutputStream.Position;
  676. baseOutputStream.Seek(headerPatchPos, SeekOrigin.Begin);
  677. WriteLeInt((int)curEntry.Crc);
  678. WriteLeInt((int)curEntry.CompressedSize);
  679. WriteLeInt((int)curEntry.Size);
  680. baseOutputStream.Seek(curPos, SeekOrigin.Begin);
  681. patchEntryHeader = false;
  682. } // if
  683. // Add data descriptor if flagged as required
  684. if ((curEntry.Flags & 8) != 0)
  685. {
  686. WriteLeInt(ZipConstants.ExternSig);
  687. WriteLeInt((int)curEntry.Crc);
  688. WriteLeInt((int)curEntry.CompressedSize);
  689. WriteLeInt((int)curEntry.Size);
  690. currentOffset += ZipConstants.ExternHeader;
  691. } // if
  692. entries.Add(curEntry);
  693. curEntry = null;
  694. }
  695. #endregion
  696. #region WriteEncryptionHeader
  697. /// <summary>
  698. /// Write encryption header
  699. /// </summary>
  700. /// <param name="crcValue">Crc value</param>
  701. private void WriteEncryptionHeader(long crcValue)
  702. {
  703. currentOffset += ZipConstants.CryptoHeaderSize;
  704. InitializePassword(Password);
  705. byte[] cryptBuffer = new byte[ZipConstants.CryptoHeaderSize];
  706. Random rnd = new Random();
  707. rnd.NextBytes(cryptBuffer);
  708. cryptBuffer[11] = (byte)(crcValue >> 24);
  709. EncryptBlock(cryptBuffer, 0, cryptBuffer.Length);
  710. baseOutputStream.Write(cryptBuffer, 0, cryptBuffer.Length);
  711. }
  712. #endregion
  713. #region AddFile
  714. /// <summary>
  715. /// Add file to zip stream.
  716. /// Will also make sure the header is updated and we store the
  717. /// correct crc and file length.
  718. /// </summary>
  719. /// <param name="filePath">File to store</param>
  720. internal void AddFile(string filePath)
  721. {
  722. // Read file data (might throw an exception if file doesn't exists)
  723. FileStream fs = File.OpenRead(filePath);
  724. byte[] buffer = new byte[fs.Length];
  725. fs.Read(buffer, 0, buffer.Length);
  726. ZipEntry entry = new ZipEntry(filePath);
  727. // Set time of storage
  728. entry.DateTime = DateTime.Now;
  729. // Set file size
  730. entry.Size = fs.Length;
  731. fs.Close();
  732. // And finally set crc value
  733. entry.Crc = Crc32.ComputeCrcValue(buffer);
  734. // Add entry
  735. PutNextEntry(entry);
  736. Write(buffer, 0, buffer.Length);
  737. CloseEntry();
  738. }
  739. #endregion
  740. #endregion
  741. }
  742. }