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
}
}