using System; using System.ComponentModel; using System.IO; using System.Text; using System.Threading; using SharpCompress.Archives; using SharpCompress.Common; using SharpCompress.Readers; using Update.Net.Events; namespace Update.Net { partial class UpdateClient { private DecompressDataStartDelegate m_DecompressDataStart; //异步解压委托 private SendOrPostCallback m_ReportDecompressProgressChanged; //解压进度报告回调 private SendOrPostCallback m_DecompressDataOperationCompleted; //解压操作完成回调 #region 事件 //解压进度 private static readonly object EVENT_DECOMPRESS_PROGRESS_CHANGED = new object(); /// /// 解压进度事件 /// public event DecompressProgressChangedEventHandler DecompressProgressChanged { add { this.Events.AddHandler(EVENT_DECOMPRESS_PROGRESS_CHANGED, value); } remove { this.Events.RemoveHandler(EVENT_DECOMPRESS_PROGRESS_CHANGED, value); } } /// /// 触发解压进度事件 /// /// 数据 protected virtual void OnDecompressProgressChanged(DecompressProgressChangedEventArgs e) { DecompressProgressChangedEventHandler handler = this.Events[EVENT_DECOMPRESS_PROGRESS_CHANGED] as DecompressProgressChangedEventHandler; if (handler != null) handler(this, e); } /// /// 触发解压进度事件 /// /// 数据 private void ReportDecompressProgressChanged(object state) { this.OnDecompressProgressChanged(state as DecompressProgressChangedEventArgs); } //解压完成 private static readonly object EVENT_DECOMPRESS_DATA_COMPLETED = new object(); /// /// 解压完成事件 /// public event DecompressDataCompletedEventHandler DecompressDataCompleted { add { this.Events.AddHandler(EVENT_DECOMPRESS_DATA_COMPLETED, value); } remove { this.Events.RemoveHandler(EVENT_DECOMPRESS_DATA_COMPLETED, value); } } /// /// 触发解压完成事件 /// /// 数据 protected virtual void OnDecompressDataCompleted(DecompressDataCompletedEventArgs e) { DecompressDataCompletedEventHandler handler = this.Events[EVENT_DECOMPRESS_DATA_COMPLETED] as DecompressDataCompletedEventHandler; if (handler != null) handler(this, e); } /// /// 触发解压完成事件 /// /// 数据 private void DecompressDataOperationCompleted(object state) { this.OnDecompressDataCompleted(state as DecompressDataCompletedEventArgs); } #endregion #region 执行回调 /// /// 执行解压进度报告回调 /// /// 进度数据 /// 异步生存期 private void PostDecompressProgressChanged(ProgressData progress, AsyncOperation asyncOp) { if (asyncOp == null) return; int progressPercentage = progress.ToComplete < 0L ? 0 : (progress.ToComplete == 0L ? 100 : (int)(100L * progress.Completed / progress.ToComplete)); DecompressProgressChangedEventArgs eventArgs = new DecompressProgressChangedEventArgs(progressPercentage, asyncOp.UserSuppliedState); this.InvokeOperation(asyncOp, this.m_ReportDecompressProgressChanged, eventArgs); } /// /// 执行解压数据完成回调 /// /// 错误 /// 是否取消 /// 异步生存期 private void DecompressDataAsyncCallback(Exception error, bool cancelled, AsyncOperation asyncOp) { DecompressDataCompletedEventArgs eventArgs = new DecompressDataCompletedEventArgs(error, cancelled, asyncOp.UserSuppliedState); this.InvokeOperationCompleted(asyncOp, this.m_DecompressDataOperationCompleted, eventArgs); } #endregion #region 工作方法 /// /// 解压数据操作 /// /// 参数 private void DecompressDataWork(DecompressDataStartArgs e) { Exception error = null; bool cancelled = false; try { using (MemoryStream stream = new MemoryStream(e.Data)) { ReaderOptions readerOptions = new ReaderOptions(); readerOptions.ArchiveEncoding.Default = Encoding.Default; using (IArchive archive = ArchiveFactory.Open(stream, readerOptions)) { this.m_Progress.ToComplete = archive.TotalUncompressSize; string deleteEntry = e.DeleteEntry; string lastEntry = e.LastEntry; string destinationDirectory = e.DestinationDirectory; ExtractionOptions extractionOptions = new ExtractionOptions { ExtractFullPath = true, Overwrite = true, PreserveFileTime = true }; IArchiveEntry last = null; foreach (IArchiveEntry entry in archive.Entries) { if (this.m_Cancelled) { cancelled = true; return; } if (entry.IsDirectory) continue; if (last == null && entry.Key.Equals(lastEntry, StringComparison.OrdinalIgnoreCase)) { last = entry; continue; } if (entry.Key.Equals(deleteEntry, StringComparison.OrdinalIgnoreCase)) DeleteFromDirectory(entry, destinationDirectory, extractionOptions); else entry.WriteToDirectory(destinationDirectory, extractionOptions); this.m_Progress.Completed += entry.Size; this.PostDecompressProgressChanged(this.m_Progress, this.m_AsyncOp); } //保证解压过程的完整性;最后处理可执行exe,保证取消后,下载过程可重现。 if (last != null) { if (this.m_Cancelled) { cancelled = true; return; } if (last.Key.Equals(deleteEntry, StringComparison.OrdinalIgnoreCase)) DeleteFromDirectory(last, destinationDirectory, extractionOptions); else last.WriteToDirectory(destinationDirectory, extractionOptions); this.m_Progress.Completed += last.Size; this.PostDecompressProgressChanged(this.m_Progress, this.m_AsyncOp); } } } } catch (Exception exp) { error = exp; } finally { this.DecompressDataAsyncCallback(error, cancelled, this.m_AsyncOp); } } /// /// 从目标目录删除压缩文档中的文件列表 /// /// 压缩文档 /// 目标目录 /// 操作选项 private void DeleteFromDirectory(IArchiveEntry entry, string destinationDirectory, ExtractionOptions options) { using (Stream stream = entry.OpenEntryStream()) { using (StreamReader reader = new StreamReader(stream, this.Encoding)) { string file; while ((file = reader.ReadLine()) != null) { file = file.Trim(); if (file.Length <= 0) continue; file = Path.Combine(destinationDirectory, options.ExtractFullPath ? file : Path.GetFileName(file)); if (Directory.Exists(file)) Directory.Delete(file, true); else File.Delete(file); } } } } #endregion #region 公共方法 /// /// 开始异步解压 /// /// 要解压的数据 /// 删除列表文件 /// 最后一个解压的文件 /// 要解压到的目录 /// 用户数据 public void DecompressDataAsync(byte[] data, string deleteEntry, string lastEntry, string destinationDirectory, object userToken = null) { if (data == null) throw new ArgumentNullException("data"); this.InitAsync(); this.ClearState(); AsyncOperation operation = AsyncOperationManager.CreateOperation(userToken); this.m_AsyncOp = operation; try { this.m_DecompressDataStart.BeginInvoke(new DecompressDataStartArgs(operation, data, deleteEntry, lastEntry, destinationDirectory), null, null); } catch (Exception error) { if (error is ThreadAbortException || error is StackOverflowException || error is OutOfMemoryException) throw; this.DecompressDataAsyncCallback(error, false, operation); } } #endregion } }