UpdateClient.DecompressData.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. using System;
  2. using System.ComponentModel;
  3. using System.IO;
  4. using System.Text;
  5. using System.Threading;
  6. using SharpCompress.Archives;
  7. using SharpCompress.Common;
  8. using SharpCompress.Readers;
  9. using Update.Net.Events;
  10. namespace Update.Net
  11. {
  12. partial class UpdateClient
  13. {
  14. private DecompressDataStartDelegate m_DecompressDataStart; //异步解压委托
  15. private SendOrPostCallback m_ReportDecompressProgressChanged; //解压进度报告回调
  16. private SendOrPostCallback m_DecompressDataOperationCompleted; //解压操作完成回调
  17. #region 事件
  18. //解压进度
  19. private static readonly object EVENT_DECOMPRESS_PROGRESS_CHANGED = new object();
  20. /// <summary>
  21. /// 解压进度事件
  22. /// </summary>
  23. public event DecompressProgressChangedEventHandler DecompressProgressChanged
  24. {
  25. add { this.Events.AddHandler(EVENT_DECOMPRESS_PROGRESS_CHANGED, value); }
  26. remove { this.Events.RemoveHandler(EVENT_DECOMPRESS_PROGRESS_CHANGED, value); }
  27. }
  28. /// <summary>
  29. /// 触发解压进度事件
  30. /// </summary>
  31. /// <param name="e">数据</param>
  32. protected virtual void OnDecompressProgressChanged(DecompressProgressChangedEventArgs e)
  33. {
  34. DecompressProgressChangedEventHandler handler = this.Events[EVENT_DECOMPRESS_PROGRESS_CHANGED] as DecompressProgressChangedEventHandler;
  35. if (handler != null)
  36. handler(this, e);
  37. }
  38. /// <summary>
  39. /// 触发解压进度事件
  40. /// </summary>
  41. /// <param name="state">数据</param>
  42. private void ReportDecompressProgressChanged(object state)
  43. {
  44. this.OnDecompressProgressChanged(state as DecompressProgressChangedEventArgs);
  45. }
  46. //解压完成
  47. private static readonly object EVENT_DECOMPRESS_DATA_COMPLETED = new object();
  48. /// <summary>
  49. /// 解压完成事件
  50. /// </summary>
  51. public event DecompressDataCompletedEventHandler DecompressDataCompleted
  52. {
  53. add { this.Events.AddHandler(EVENT_DECOMPRESS_DATA_COMPLETED, value); }
  54. remove { this.Events.RemoveHandler(EVENT_DECOMPRESS_DATA_COMPLETED, value); }
  55. }
  56. /// <summary>
  57. /// 触发解压完成事件
  58. /// </summary>
  59. /// <param name="e">数据</param>
  60. protected virtual void OnDecompressDataCompleted(DecompressDataCompletedEventArgs e)
  61. {
  62. DecompressDataCompletedEventHandler handler = this.Events[EVENT_DECOMPRESS_DATA_COMPLETED] as DecompressDataCompletedEventHandler;
  63. if (handler != null)
  64. handler(this, e);
  65. }
  66. /// <summary>
  67. /// 触发解压完成事件
  68. /// </summary>
  69. /// <param name="state">数据</param>
  70. private void DecompressDataOperationCompleted(object state)
  71. {
  72. this.OnDecompressDataCompleted(state as DecompressDataCompletedEventArgs);
  73. }
  74. #endregion
  75. #region 执行回调
  76. /// <summary>
  77. /// 执行解压进度报告回调
  78. /// </summary>
  79. /// <param name="progress">进度数据</param>
  80. /// <param name="asyncOp">异步生存期</param>
  81. private void PostDecompressProgressChanged(ProgressData progress, AsyncOperation asyncOp)
  82. {
  83. if (asyncOp == null)
  84. return;
  85. int progressPercentage = progress.ToComplete < 0L ? 0 : (progress.ToComplete == 0L ? 100 : (int)(100L * progress.Completed / progress.ToComplete));
  86. DecompressProgressChangedEventArgs eventArgs = new DecompressProgressChangedEventArgs(progressPercentage, asyncOp.UserSuppliedState);
  87. this.InvokeOperation(asyncOp, this.m_ReportDecompressProgressChanged, eventArgs);
  88. }
  89. /// <summary>
  90. /// 执行解压数据完成回调
  91. /// </summary>
  92. /// <param name="error">错误</param>
  93. /// <param name="cancelled">是否取消</param>
  94. /// <param name="asyncOp">异步生存期</param>
  95. private void DecompressDataAsyncCallback(Exception error, bool cancelled, AsyncOperation asyncOp)
  96. {
  97. DecompressDataCompletedEventArgs eventArgs = new DecompressDataCompletedEventArgs(error, cancelled, asyncOp.UserSuppliedState);
  98. this.InvokeOperationCompleted(asyncOp, this.m_DecompressDataOperationCompleted, eventArgs);
  99. }
  100. #endregion
  101. #region 工作方法
  102. /// <summary>
  103. /// 解压数据操作
  104. /// </summary>
  105. /// <param name="e">参数</param>
  106. private void DecompressDataWork(DecompressDataStartArgs e)
  107. {
  108. Exception error = null;
  109. bool cancelled = false;
  110. try
  111. {
  112. using (MemoryStream stream = new MemoryStream(e.Data))
  113. {
  114. ReaderOptions readerOptions = new ReaderOptions();
  115. readerOptions.ArchiveEncoding.Default = Encoding.Default;
  116. using (IArchive archive = ArchiveFactory.Open(stream, readerOptions))
  117. {
  118. this.m_Progress.ToComplete = archive.TotalUncompressSize;
  119. string deleteEntry = e.DeleteEntry;
  120. string lastEntry = e.LastEntry;
  121. string destinationDirectory = e.DestinationDirectory;
  122. ExtractionOptions extractionOptions = new ExtractionOptions { ExtractFullPath = true, Overwrite = true, PreserveFileTime = true };
  123. IArchiveEntry last = null;
  124. foreach (IArchiveEntry entry in archive.Entries)
  125. {
  126. if (this.m_Cancelled)
  127. {
  128. cancelled = true;
  129. return;
  130. }
  131. if (entry.IsDirectory)
  132. continue;
  133. if (last == null && entry.Key.Equals(lastEntry, StringComparison.OrdinalIgnoreCase))
  134. {
  135. last = entry;
  136. continue;
  137. }
  138. if (entry.Key.Equals(deleteEntry, StringComparison.OrdinalIgnoreCase))
  139. DeleteFromDirectory(entry, destinationDirectory, extractionOptions);
  140. else
  141. entry.WriteToDirectory(destinationDirectory, extractionOptions);
  142. this.m_Progress.Completed += entry.Size;
  143. this.PostDecompressProgressChanged(this.m_Progress, this.m_AsyncOp);
  144. }
  145. //保证解压过程的完整性;最后处理可执行exe,保证取消后,下载过程可重现。
  146. if (last != null)
  147. {
  148. if (this.m_Cancelled)
  149. {
  150. cancelled = true;
  151. return;
  152. }
  153. if (last.Key.Equals(deleteEntry, StringComparison.OrdinalIgnoreCase))
  154. DeleteFromDirectory(last, destinationDirectory, extractionOptions);
  155. else
  156. last.WriteToDirectory(destinationDirectory, extractionOptions);
  157. this.m_Progress.Completed += last.Size;
  158. this.PostDecompressProgressChanged(this.m_Progress, this.m_AsyncOp);
  159. }
  160. }
  161. }
  162. }
  163. catch (Exception exp)
  164. {
  165. error = exp;
  166. }
  167. finally
  168. {
  169. this.DecompressDataAsyncCallback(error, cancelled, this.m_AsyncOp);
  170. }
  171. }
  172. /// <summary>
  173. /// 从目标目录删除压缩文档中的文件列表
  174. /// </summary>
  175. /// <param name="entry">压缩文档</param>
  176. /// <param name="destinationDirectory">目标目录</param>
  177. /// <param name="options">操作选项</param>
  178. private void DeleteFromDirectory(IArchiveEntry entry, string destinationDirectory, ExtractionOptions options)
  179. {
  180. using (Stream stream = entry.OpenEntryStream())
  181. {
  182. using (StreamReader reader = new StreamReader(stream, this.Encoding))
  183. {
  184. string file;
  185. while ((file = reader.ReadLine()) != null)
  186. {
  187. file = file.Trim();
  188. if (file.Length <= 0)
  189. continue;
  190. file = Path.Combine(destinationDirectory, options.ExtractFullPath ? file : Path.GetFileName(file));
  191. if (Directory.Exists(file))
  192. Directory.Delete(file, true);
  193. else
  194. File.Delete(file);
  195. }
  196. }
  197. }
  198. }
  199. #endregion
  200. #region 公共方法
  201. /// <summary>
  202. /// 开始异步解压
  203. /// </summary>
  204. /// <param name="data">要解压的数据</param>
  205. /// <param name="deleteEntry">删除列表文件</param>
  206. /// <param name="lastEntry">最后一个解压的文件</param>
  207. /// <param name="destinationDirectory">要解压到的目录</param>
  208. /// <param name="userToken">用户数据</param>
  209. public void DecompressDataAsync(byte[] data, string deleteEntry, string lastEntry, string destinationDirectory, object userToken = null)
  210. {
  211. if (data == null)
  212. throw new ArgumentNullException("data");
  213. this.InitAsync();
  214. this.ClearState();
  215. AsyncOperation operation = AsyncOperationManager.CreateOperation(userToken);
  216. this.m_AsyncOp = operation;
  217. try
  218. {
  219. this.m_DecompressDataStart.BeginInvoke(new DecompressDataStartArgs(operation, data, deleteEntry, lastEntry, destinationDirectory), null, null);
  220. }
  221. catch (Exception error)
  222. {
  223. if (error is ThreadAbortException || error is StackOverflowException || error is OutOfMemoryException)
  224. throw;
  225. this.DecompressDataAsyncCallback(error, false, operation);
  226. }
  227. }
  228. #endregion
  229. }
  230. }