AbstractArchive.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using SharpCompress.Common;
  6. using SharpCompress.Readers;
  7. namespace SharpCompress.Archives
  8. {
  9. public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtractionListener
  10. where TEntry : IArchiveEntry
  11. where TVolume : IVolume
  12. {
  13. private readonly LazyReadOnlyCollection<TVolume> lazyVolumes;
  14. private readonly LazyReadOnlyCollection<TEntry> lazyEntries;
  15. public event EventHandler<ArchiveExtractionEventArgs<IArchiveEntry>> EntryExtractionBegin;
  16. public event EventHandler<ArchiveExtractionEventArgs<IArchiveEntry>> EntryExtractionEnd;
  17. public event EventHandler<CompressedBytesReadEventArgs> CompressedBytesRead;
  18. public event EventHandler<FilePartExtractionBeginEventArgs> FilePartExtractionBegin;
  19. protected ReaderOptions ReaderOptions { get; }
  20. private bool disposed;
  21. #if !NO_FILE
  22. internal AbstractArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerOptions)
  23. {
  24. Type = type;
  25. if (!fileInfo.Exists)
  26. {
  27. throw new ArgumentException("File does not exist: " + fileInfo.FullName);
  28. }
  29. ReaderOptions = readerOptions;
  30. readerOptions.LeaveStreamOpen = false;
  31. lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(fileInfo));
  32. lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
  33. }
  34. protected abstract IEnumerable<TVolume> LoadVolumes(FileInfo file);
  35. #endif
  36. internal AbstractArchive(ArchiveType type, IEnumerable<Stream> streams, ReaderOptions readerOptions)
  37. {
  38. Type = type;
  39. ReaderOptions = readerOptions;
  40. lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(streams.Select(CheckStreams)));
  41. lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
  42. }
  43. internal AbstractArchive(ArchiveType type)
  44. {
  45. Type = type;
  46. lazyVolumes = new LazyReadOnlyCollection<TVolume>(Enumerable.Empty<TVolume>());
  47. lazyEntries = new LazyReadOnlyCollection<TEntry>(Enumerable.Empty<TEntry>());
  48. }
  49. public ArchiveType Type { get; }
  50. void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry)
  51. {
  52. EntryExtractionBegin?.Invoke(this, new ArchiveExtractionEventArgs<IArchiveEntry>(entry));
  53. }
  54. void IArchiveExtractionListener.FireEntryExtractionEnd(IArchiveEntry entry)
  55. {
  56. EntryExtractionEnd?.Invoke(this, new ArchiveExtractionEventArgs<IArchiveEntry>(entry));
  57. }
  58. private static Stream CheckStreams(Stream stream)
  59. {
  60. if (!stream.CanSeek || !stream.CanRead)
  61. {
  62. throw new ArgumentException("Archive streams must be Readable and Seekable");
  63. }
  64. return stream;
  65. }
  66. /// <summary>
  67. /// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive.
  68. /// </summary>
  69. public virtual ICollection<TEntry> Entries { get { return lazyEntries; } }
  70. /// <summary>
  71. /// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive.
  72. /// </summary>
  73. public ICollection<TVolume> Volumes { get { return lazyVolumes; } }
  74. /// <summary>
  75. /// The total size of the files compressed in the archive.
  76. /// </summary>
  77. public virtual long TotalSize { get { return Entries.Aggregate(0L, (total, cf) => total + cf.CompressedSize); } }
  78. /// <summary>
  79. /// The total size of the files as uncompressed in the archive.
  80. /// </summary>
  81. public virtual long TotalUncompressSize { get { return Entries.Aggregate(0L, (total, cf) => total + cf.Size); } }
  82. protected abstract IEnumerable<TVolume> LoadVolumes(IEnumerable<Stream> streams);
  83. protected abstract IEnumerable<TEntry> LoadEntries(IEnumerable<TVolume> volumes);
  84. IEnumerable<IArchiveEntry> IArchive.Entries { get { return Entries.Cast<IArchiveEntry>(); } }
  85. IEnumerable<IVolume> IArchive.Volumes { get { return lazyVolumes.Cast<IVolume>(); } }
  86. public virtual void Dispose()
  87. {
  88. if (!disposed)
  89. {
  90. lazyVolumes.ForEach(v => v.Dispose());
  91. lazyEntries.GetLoaded().Cast<Entry>().ForEach(x => x.Close());
  92. disposed = true;
  93. }
  94. }
  95. void IArchiveExtractionListener.EnsureEntriesLoaded()
  96. {
  97. lazyEntries.EnsureFullyLoaded();
  98. lazyVolumes.EnsureFullyLoaded();
  99. }
  100. void IExtractionListener.FireCompressedBytesRead(long currentPartCompressedBytes, long compressedReadBytes)
  101. {
  102. CompressedBytesRead?.Invoke(this, new CompressedBytesReadEventArgs
  103. {
  104. CurrentFilePartCompressedBytesRead = currentPartCompressedBytes,
  105. CompressedBytesRead = compressedReadBytes
  106. });
  107. }
  108. void IExtractionListener.FireFilePartExtractionBegin(string name, long size, long compressedSize)
  109. {
  110. FilePartExtractionBegin?.Invoke(this, new FilePartExtractionBeginEventArgs
  111. {
  112. CompressedSize = compressedSize,
  113. Size = size,
  114. Name = name
  115. });
  116. }
  117. /// <summary>
  118. /// Use this method to extract all entries in an archive in order.
  119. /// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be
  120. /// extracted sequentially for the best performance.
  121. ///
  122. /// This method will load all entry information from the archive.
  123. ///
  124. /// WARNING: this will reuse the underlying stream for the archive. Errors may
  125. /// occur if this is used at the same time as other extraction methods on this instance.
  126. /// </summary>
  127. /// <returns></returns>
  128. public IReader ExtractAllEntries()
  129. {
  130. ((IArchiveExtractionListener)this).EnsureEntriesLoaded();
  131. return CreateReaderForSolidExtraction();
  132. }
  133. protected abstract IReader CreateReaderForSolidExtraction();
  134. /// <summary>
  135. /// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files).
  136. /// </summary>
  137. public virtual bool IsSolid { get { return false; } }
  138. /// <summary>
  139. /// The archive can find all the parts of the archive needed to fully extract the archive. This forces the parsing of the entire archive.
  140. /// </summary>
  141. public bool IsComplete
  142. {
  143. get
  144. {
  145. ((IArchiveExtractionListener)this).EnsureEntriesLoaded();
  146. return Entries.All(x => x.IsComplete);
  147. }
  148. }
  149. }
  150. }