using System; using System.Collections.Generic; using System.Text; using System.Net; using System.ComponentModel; using System.Xml.Serialization; using System.IO; using System.Threading; using System.Collections.Specialized; namespace ProjectBase.Data.Update { /// /// 下载错误事件数据 /// public class DownloadErrorEventArgs : EventArgs { public Exception Error { get; set; } public Manifest Manifest { get; set; } } /// /// 下载进度事件数据 /// public class DownloadProgressEventArgs : ProgressChangedEventArgs { public DownloadProgressEventArgs(int progressPercentage, object userState) : base(progressPercentage,userState) { } /// /// 当前下载的文件名 /// public string FileName { get; set; } /// /// 获取收到的字节数。 /// public long BytesReceived { get; set; } /// /// 获取 System.Net.WebClient 数据下载操作中的字节总数。 /// public long TotalBytesToReceive { get; set; } } /// /// 下载完成事件数据 /// public class DownloadCompleteEventArgs : AsyncCompletedEventArgs { public DownloadCompleteEventArgs(Exception error, bool cancelled, object userState) : base(error, cancelled, userState) { } public Manifest Manifest { get; set; } } /// /// 服务器文件下载类 /// public class DownloadClass : Component { #region 变量定义 private WebClient webClient = new WebClient(); private Manifest manifest; private int fileCount = 0; // 文件数量 private long fileSize = 0; // 每个文件大小 private bool cancel = false; private string tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../temp"); private HybridDictionary userStateToLifetime = new HybridDictionary(); private object defaultTaskId = new object(); private delegate void WorkerEventHandler(AsyncOperation asyncOp); private System.ComponentModel.Container components = null; private SendOrPostCallback onProgressReportDelegate; private SendOrPostCallback onCompletedDelegate; private AsyncOperation current; #endregion #region 事件 /// /// 下载进度 /// public event EventHandler DownloadProgressChanged; /// /// 下载完成事件 /// public event EventHandler DownloadCompleted; /// /// 下载错误触发的事件 /// public event EventHandler DownloadError; #endregion #region 构造及析构 public DownloadClass(IContainer container) { container.Add(this); InitializeComponent(); InitializeDelegates(); } public DownloadClass() { InitializeComponent(); InitializeDelegates(); } /// /// 初始化代理 /// protected virtual void InitializeDelegates() { onProgressReportDelegate = new SendOrPostCallback(ReportProgress); onCompletedDelegate = new SendOrPostCallback(DoDownloadCompleted); } private void InitializeComponent() { components = new System.ComponentModel.Container(); } protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } #endregion /// /// 触发下载进度事件 /// /// protected virtual void OnDownloadProgressChanged(DownloadProgressEventArgs e) { if (DownloadProgressChanged != null) { DownloadProgressChanged(this, e); } } /// /// 触发下载完成事件 /// /// protected virtual void OnDownloadCompleted(DownloadCompleteEventArgs e) { if (DownloadCompleted != null) { DownloadCompleted(this, e); } } /// /// 触发下载错误事件 /// /// protected virtual void OnDownloadError(DownloadErrorEventArgs e) { if (DownloadError != null) { DownloadError(this, e); } } /// /// 下载文字保存的临时目录 /// public string TempPath { get { return tempPath; } set { tempPath = value; } } /// /// 同步下载 /// /// 文件下载清单 public void Download(Manifest manifest) { Init(manifest); foreach (var file in manifest.ManifestFiles.Files) { string serverFileName = Path.Combine(manifest.ManifestFiles.BaseUrl, file.Source); string clientFileName = Path.Combine(tempPath, file.Source); Uri uri = new Uri(serverFileName); if (!Directory.Exists(Path.GetDirectoryName(clientFileName))) { Directory.CreateDirectory(Path.GetDirectoryName(clientFileName)); } webClient.DownloadFile(uri, clientFileName); } } /// /// 异步下载 /// /// 文件下载清单 public void DownloadAsync(Manifest manifest) { Init(manifest); DownloadAsync(manifest,defaultTaskId); } /// /// 异步下载并指定任务Id /// /// 文件下载清单 /// 任务Id public void DownloadAsync(Manifest manifest,object taskId) { AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId); lock (userStateToLifetime.SyncRoot) { if (userStateToLifetime.Contains(taskId)) { throw new ArgumentException("参数taskId必须是唯一的", "taskId"); } userStateToLifetime[taskId] = asyncOp; } WorkerEventHandler workerDelegate = new WorkerEventHandler(DownloadWorker); workerDelegate.BeginInvoke(asyncOp, null, null); } private void Init(Manifest mainfest) { this.manifest = mainfest; webClient.BaseAddress = manifest.ManifestFiles.BaseUrl; webClient.Credentials = new NetworkCredential(mainfest.FtpName, mainfest.FtpPsswd); webClient.Encoding = Encoding.UTF8; } /// /// 异步下载方法 /// /// private void DownloadWorker(AsyncOperation asyncOp) { current = asyncOp; if (!TaskCanceled(asyncOp.UserSuppliedState)) { try { webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(webClient_DownloadFileCompleted); webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged); foreach (var file in manifest.ManifestFiles.Files) { string serverFileName = Path.Combine(manifest.ManifestFiles.BaseUrl, file.Source); string clientFileName = Path.Combine(tempPath, file.Source); Uri uri = new Uri(serverFileName); if (!Directory.Exists(Path.GetDirectoryName(clientFileName))) { Directory.CreateDirectory(Path.GetDirectoryName(clientFileName)); } WebRequest sizeRequest = WebRequest.Create(serverFileName); sizeRequest.Credentials = new NetworkCredential(manifest.FtpName, manifest.FtpPsswd); ; sizeRequest.Method = WebRequestMethods.Ftp.GetFileSize; fileSize += (int)sizeRequest.GetResponse().ContentLength; while (webClient.IsBusy) { //阻塞异步下载 } if (!cancel) { webClient.DownloadFileAsync(uri, clientFileName, file.Source); } } } catch (Exception ex) { DownloadErrorEventArgs e = new DownloadErrorEventArgs(); e.Error = ex; e.Manifest = manifest; OnDownloadError(e); } } } /// /// 异步完成方法 /// /// 异常数据 /// 是否取消 /// private void CompletionMethod(Exception exception, bool canceled, AsyncOperation asyncOp) { if (!canceled) { lock (userStateToLifetime.SyncRoot) { userStateToLifetime.Remove(asyncOp.UserSuppliedState); } } DownloadCompleteEventArgs e = new DownloadCompleteEventArgs(exception, canceled, asyncOp.UserSuppliedState); e.Manifest = manifest; asyncOp.PostOperationCompleted(onCompletedDelegate, e); current = null; } /// /// 异步下载进度事件(仅对于单个文件) /// void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { DownloadProgressEventArgs args = new DownloadProgressEventArgs((int)((double)e.BytesReceived / (double)fileSize * 100), e.UserState); args.BytesReceived += e.BytesReceived; args.FileName = e.UserState.ToString(); args.TotalBytesToReceive = fileSize; if (current != null) { current.Post(onProgressReportDelegate, args); } } /// /// 异步下载完成事件(仅对于单个文件) /// /// /// void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { fileCount++; if (fileCount == manifest.ManifestFiles.Files.Length) { this.CompletionMethod(e.Error, TaskCanceled(current.UserSuppliedState), current); } } /// /// 取消异步下载 /// public void CancelAsync() { CancelAsync(defaultTaskId); } /// /// 取消异步下载 /// public void CancelAsync(object taskId) { webClient.CancelAsync(); cancel = true; current = null; AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation; if (asyncOp != null) { lock (userStateToLifetime.SyncRoot) { userStateToLifetime.Remove(taskId); } } } private bool TaskCanceled(object taskId) { return cancel || (userStateToLifetime[taskId] == null); } private void DoDownloadCompleted(object operationState) { DownloadCompleteEventArgs e = operationState as DownloadCompleteEventArgs; OnDownloadCompleted(e); } private void ReportProgress(object state) { DownloadProgressEventArgs e = state as DownloadProgressEventArgs; OnDownloadProgressChanged(e); } } }