/// <summary> 执行下载 </summary> bool DownloadPackages(Wrapper.RunworkEventArgs rt) { System.IO.Directory.CreateDirectory(Context.UpdatePackagePath); Trace.TraceInformation("开始下载网络更新包"); var workerCount = Math.Max(1, Context.MultipleDownloadCount); var workers = new List<WebClient>(workerCount); var evt = new AutoResetEvent(false); var hasError = false; //download redirect if (!string.IsNullOrEmpty(Context.UpdateInfo.PackageUrlTemplate)) { Trace.TraceInformation("已经重定向下载包地址到 {0}", Context.UpdateInfo.PackageUrlTemplate); Context.UpdateDownloadUrl = Context.UpdateInfo.PackageUrlTemplate; } //Ping if (!string.IsNullOrEmpty(Context.UpdateInfo.UpdatePingUrl)) { try { Context.CreateWebClient().UploadData(new Uri(Context.UpdateInfo.UpdatePingUrl), new byte[0]); } catch (Exception) { } } //生成下载队列 Trace.TraceInformation("正在初始化 {0} 个WebClient", workerCount); for (var i = 0; i < workerCount; i++) { var clnt = Context.CreateWebClient(); clnt.DownloadFileCompleted += (s, e) => { var pkg = e.UserState as PackageInfo; var cnt = s as WebClient; pkg.LastError = e.Error; if (e.Error != null) { Trace.TraceWarning("包【" + pkg.PackageName + "】下载失败:" + e.Error.Message); rt.PostEvent(PackageDownloadFailed, this, new PackageEventArgs(pkg)); } else if (pkg.IsLocalFileValid != true) { Trace.TraceWarning("包【" + pkg.PackageName + "】MD5校验失败", "错误"); pkg.LastError = new Exception("不文件哈希值不正确或文件不存在"); rt.PostEvent(PackageHashMismatch, this, new PackageEventArgs(pkg)); } if (pkg.LastError != null) { //如果出错,且重试次数在限制范围内,则重试 pkg.IncreaseFailureCounter(); if (pkg.RetryCount <= Context.MaxiumRetryDownloadCount) { Trace.TraceWarning("包【" + pkg.PackageName + "】未能成功下载,正在进行第 " + pkg.RetryCount + " 次重试,最大重试次数为 " + Context.MaxiumRetryDownloadCount, "错误"); cnt.DownloadFileAsync(new Uri(pkg.SourceUri), pkg.LocalSavePath, pkg); rt.PostEvent(PackageDownloadRetried, this, new PackageEventArgs(pkg)); return; } //标记出错 hasError = true; } //包下载完成事件 pkg.IsDownloading = false; pkg.IsDownloaded = pkg.LastError == null; rt.PostEvent(PackageDownloadFinished, this, new PackageEventArgs(e.UserState as PackageInfo)); lock (PackagesToUpdate) { Trace.TraceInformation("包【" + pkg.PackageName + "】下载操作完成:" + (pkg.IsDownloaded ? "下载成功" : "下载失败")); evt.Set(); } }; clnt.DownloadProgressChanged += (s, e) => { var pkg = e.UserState as PackageInfo; pkg.DownloadedSize = e.BytesReceived; pkg.PackageSize = e.TotalBytesToReceive > 0 ? e.TotalBytesToReceive : pkg.PackageSize; rt.PostEvent(DownloadProgressChanged, this, new PackageDownloadProgressChangedEventArgs(pkg, pkg.PackageSize, pkg.DownloadedSize, e.ProgressPercentage)); }; workers.Add(clnt); } //开始处理事务 while (!hasError) { var breakFlag = false; lock (PackagesToUpdate) { //没有错误,则分配下个任务 WebClient client; while ((client = workers.Find(s => !s.IsBusy)) != null) { var nextPkg = PackagesToUpdate.Find(s => !s.IsDownloading && !s.IsDownloaded); if (nextPkg == null) { breakFlag = true; break; } nextPkg.IsDownloading = true; Trace.TraceInformation("包【" + nextPkg.PackageName + "】开始下载"); rt.PostEvent(PackageDownload, this, new PackageEventArgs(nextPkg)); client.DownloadFileAsync(new Uri(Context.RandomUrl(Context.GetUpdatePackageFullUrl(nextPkg.PackageName))), nextPkg.LocalSavePath, nextPkg); } } if (breakFlag) break; evt.WaitOne(); Trace.TraceInformation("线程同步事件已收到"); } //不管任何原因中止下载,到这里时都需要等待所有客户端中止 while (true) { //出错了,那么对所有的客户端发出中止命令。这里不需要判断是否忙碌。 if (hasError) { Trace.TraceWarning("出现错误,正在取消所有包的下载队列"); workers.ForEach(s => s.CancelAsync()); } lock (PackagesToUpdate) { Trace.TraceInformation("等待下载队列完成操作"); if (workers.FindIndex(s => s.IsBusy) == -1) break; } evt.WaitOne(); } Trace.TraceInformation("完成下载网络更新包"); var errorPkgs = ExtensionMethod.ToList(ExtensionMethod.Where(PackagesToUpdate, s => s.LastError != null)); if (errorPkgs.Count > 0) throw new PackageDownloadException(errorPkgs.ToArray()); return !hasError; }