[C#] 下载器与上传器使用示例
由于下载器与上传器实现原理相似,所以仅以下载器举例。
优势:
- 创建多个下载器,例如负责数据管理模块的、负责专家服务的等;
- 同时多个下载任务,例如可设置数据管理同时下载多个,而非逐个排队下载;
- 多个下载任务可组合成一个下载任务组,一个下载任务可加入多个组,加入多个组不会重复下载;(加入多个组理论可行,但有些代码要完善。)
- 下载任务及下载任务组都有进度值、下载速度,有下载进度改变通知、下载结束通知;
- 下载过程中可处理预下载事件,可中途插入或移除下载任务;
- 支持自定义的下载方式,只需实现抽象类,目前已提供常规的HTTP、FTP、SFTP、HTTP断点下载方式。
注:组仅是下载任务的整体统计,与下载器关系不大;
下面逐一举例。
示例一:创建负责不同模块的下载器。
var downloadEngine1 = DownloadEngine.Create("数据管理", 1, out var createNew); // createNew = False var downloadEngine2 = DownloadEngine.Create("专家服务", 1, out createNew); // createNew = False var downloadEngine3 = DownloadEngine.Create("数据管理", 2, out createNew); // createNew = True,downloadEngine3 = downloadEngine1,同时下载数 = 1
示例二:多个下载任务同时下载。
// 创建3个任务同时下载的下载器 var downloadEngine = DownloadEngine.Create("数据管理", 3, out _);
示例三:多个下载任务组合成一个下载任务组。
var downloadEngine = DownloadEngine.Create("下载器名称", 1, out _); var guid = Guid.NewGuid().ToString(); // 获取指定下载任务组,如果不存在则创建下载任务组 var downloadTaskGroup = downloadEngine.GetOrAdd(q => q.Code == guid, () => { // 创建三个不支持计算下载速度的下载任务 var downloadTasks = new List<DownloadTask> { new DownloadTask{ Url="http://xxx.xxx/files/1.zip" }, new DownloadTask{ Url="http://xxx.xxx/files/2.zip" }, new DownloadTask{ Url="http://xxx.xxx/files/3.zip" } }; // 创建一个支持计算下载速度的下载任务组 return new DownloadTaskGroup(downloadTasks, true) { Code = guid }; });
示例四:更新下载任务的进度值、下载速度,处理下载进度改变通知、下载结束通知。
var downloadEngine = DownloadEngine.Create("下载器名称", 1, out _); var guid = Guid.NewGuid().ToString(); var downloadTask = downloadEngine.GetOrAdd(q => q.Code == guid, () => new DownloadTask(true) // true:计算下载速度 { Code = guid, Url = "http://xxx.xxx/files/1.zip" }); downloadTask.DownloadProgressChanged += OnDownloadProgressChanged; // 处理下载进度改变通知 downloadTask.DownloadFileCompleted += OnDownloadFileCompleted; // 处理下载结束通知,可能是取消下载、下载失败、下载成功三种结果
示例五:处理下载任务的预下载通知,及中途插入和移除下载任务。
var downloadEngine = DownloadEngine.Create("下载器名称", 1, out _); var guid = Guid.NewGuid().ToString(); var downloadTaskGroup = downloadEngine.GetOrAdd(q => q.Code == guid, () => { var downloadTask1 = new DownloadTask { Name = "下载1" }; var downloadTask2 = new DownloadTask { Name = "下载2" }; downloadTask1.PreviewDownload += delegate { // 如果此处返回失败或异常,则下载失败,及取消所在下载任务组 // 在预下载时补充下载信息,比如在预上传时,才生成上传文件 downloadTask1.Url = "http://xxx.xxx/files/1.zip"; // 插入下载任务 var downloadTask3 = new DownloadTask { Name = "下载3" }; downloadTask1.Group.Children.Add(downloadTask3); // 加入所在任务组 downloadEngine.Unshift(downloadTask3); // 插入到最前面 return ResultValue.Succeed; // 返回成功 }; downloadTask1.DownloadFileCompleted += delegate { if (downloadTask1.DownloadStatus == DownloadStatus.Completed) { // 例如下载检测包后,发现不需要下载安装包 downloadEngine.RemoveWaitingTask(downloadTask2); // 只能移除还未开始下载的任务 downloadTask1.Group.Children.Remove(downloadTask2); // 同时在任务组中移除 } }; return new DownloadTaskGroup(new DownloadTask[] { downloadTask1, downloadTask2 }, true) { Code = guid }; });
示例六:支持HTTP、FTP、SFTP、自定义下载方式。
var downloadEngine = DownloadEngine.Create("下载器名称", 1, out _); var guid = Guid.NewGuid().ToString(); var downloadTaskGroup = downloadEngine.GetOrAdd(q => q.Code == guid, () => { var downloadTask1 = new DownloadTask { Name = "HTTP下载", Url = "http://xxx.xxx/files/1.zip", // 下载链接 Headers = new Dictionary<string, string>(), // 设置请求头 }; downloadTask1.CreateDownloadClient = () => new HttpDownloadClient(downloadTask1); // HTTP是默认下载方式,可以不设置 var downloadTask2 = new DownloadTask { Name = "FTP下载", Url = "ftp://192.168.0.1:22/files/1.zip", // 下载链接 Host = "192.168.0.1:22", // FTP主机地址 Port = 22, // 端口号 UserName = "admin", // 用户名 Password = "123456" // 密码 }; downloadTask2.CreateDownloadClient = () => new FtpDownloadClient(downloadTask2); var downloadTask3 = new DownloadTask { Name = "SFTP下载", Url = "./files/1.zip", // 下载链接 Host = "192.168.0.1:22", // SFTP主机地址 Port = 22, // 端口号 UserName = "admin", // 用户名 Password = "123456" // 密码 }; downloadTask3.CreateDownloadClient = () => new SftpDownloadClient(downloadTask3); var downloadTask4 = new DownloadTask { Name = "自定义下载", Url = "http://xxx.xxx/files/1.zip", // 下载链接 Headers = new Dictionary<string, string>(), // 设置请求头 }; downloadTask4.CreateDownloadClient = () => new CustomDownloadClient(downloadTask4); // 使用自定义方式下载,例如HTTP断点续传 return new DownloadTaskGroup(new DownloadTask[] { downloadTask1, downloadTask2, downloadTask3 }, true) { Code = guid }; }); /// <summary> /// 未实现的自定义下载方式 /// </summary> public class CustomDownloadClient : DownloadClientBase { public CustomDownloadClient(DownloadTask downloadTask) : base(downloadTask) { throw new NotImplementedException(); } public override void BeginCancel() { throw new NotImplementedException(); } public override void BeginDownload() { throw new NotImplementedException(); } public override void Dispose() { throw new NotImplementedException(); } public override ResultValue<long> GetFileSize() { throw new NotImplementedException(); } }
示例七:支持HTTP断点下载。
var downloadEngine = DownloadEngine.Create("下载器名称", 1, out _); var guid = Guid.NewGuid().ToString(); var downloadTask = downloadEngine.GetOrAdd(q => q.Code == guid, () => { var download = new DownloadTask(true) // true:计算下载速度 { Code = guid, Url = "http://xxx.xxx/files/1.zip" }; download.CreateDownloadClient = () => new HttpRangeDownloadClient(download); // HTTP断点下载 });
示例八:监控不同下载任务的更新进度,即使在不同的下载任务组。
var watcher = new DownloadProgressWatcher(); // 下载进度监听器 watcher.DownloadProgressChanged += OnDownloadProgressChanged; watcher.DownloadFileCompleted += OnDownloadFileCompleted; watcher.AppendDownloadTaskGroup(downloadTaskGroup); // 添加下载任务组 watcher.AppendDownloadTask(downloadTask); // 添加下载任务
下载器关键源码:

1 using Gnt.DTOs; 2 using Gnt.Events; 3 using Gnt.Events.PubSubEvents; 4 using Gnt.Extensions; 5 using Gnt.Ioc; 6 using Gnt.Mvvm; 7 using Renci.SshNet; 8 using System; 9 using System.Collections.Generic; 10 using System.ComponentModel; 11 using System.IO; 12 using System.Linq; 13 using System.Net; 14 using System.Text; 15 using System.Threading; 16 using System.Threading.Tasks; 17 18 namespace Gnt.Utils 19 { 20 /// <summary> 21 /// 下载器 22 /// </summary> 23 public sealed class DownloadEngine : IDisposable 24 { 25 #region Static 26 27 /// <summary> 28 /// 已创建下载器 29 /// </summary> 30 private static Dictionary<string, DownloadEngine> _downloadEngines = new Dictionary<string, DownloadEngine>(); 31 32 /// <summary> 33 /// 创建下载器 34 /// </summary> 35 /// <param name="name">下载器名称</param> 36 /// <param name="maximumCount">最大同时下载数</param> 37 /// <param name="createdNew">是否创建了新的</param> 38 /// <returns></returns> 39 public static DownloadEngine Create(string name, int maximumCount, out bool createdNew) 40 { 41 lock (_downloadEngines) 42 { 43 createdNew = false; 44 45 if (_downloadEngines.TryGetValue(name, out DownloadEngine downloadEngine)) 46 return downloadEngine; 47 48 downloadEngine = new DownloadEngine(name, maximumCount); 49 _downloadEngines.Add(name, downloadEngine); 50 51 createdNew = true; 52 return downloadEngine; 53 } 54 } 55 56 /// <summary> 57 /// 获取下载器 58 /// </summary> 59 /// <param name="name"></param> 60 /// <returns></returns> 61 public static DownloadEngine Get(string name) 62 { 63 lock (_downloadEngines) 64 { 65 if (_downloadEngines.TryGetValue(name, out DownloadEngine downloadEngine)) 66 return downloadEngine; 67 return null; 68 } 69 } 70 71 #endregion 72 73 /// <summary> 74 /// 下载器 75 /// </summary> 76 /// <param name="name">下载器名称</param> 77 /// <param name="maximumCount">最大同时下载数</param> 78 private DownloadEngine(string name, int maximumCount) 79 { 80 Name = name; 81 _maximumCount = maximumCount; 82 _semaphore = new Semaphore(maximumCount, maximumCount); 83 } 84 85 #region Method 86 87 /// <summary> 88 /// 释放资源 89 /// </summary> 90 public void Dispose() 91 { 92 lock (locker) 93 { 94 foreach (var item in _waiting.ToArray()) 95 { 96 Remove(item); 97 } 98 foreach (var item in _downloading.Keys.ToArray()) 99 { 100 Remove(item); 101 } 102 } 103 104 lock (_downloadEngines) 105 { 106 if (_semaphore != null) 107 { 108 _semaphore.Dispose(); 109 _semaphore = null; 110 } 111 112 _downloadEngines.Remove(Name); 113 } 114 } 115 116 /// <summary> 117 /// 加入下载任务 118 /// </summary> 119 /// <param name="downloadTask"></param> 120 public void Add(DownloadTask downloadTask) 121 { 122 lock (locker) 123 { 124 if (_downloading.ContainsKey(downloadTask) || _waiting.Contains(downloadTask)) 125 return; 126 127 _waiting.Add(downloadTask); 128 downloadTask.DownloadEngine = this; 129 130 if (_thread == null) 131 { 132 // 启动轮询线程 133 _thread = new Thread(new ThreadStart(Run)) { IsBackground = true }; 134 _thread.Start(); 135 } 136 } 137 } 138 139 /// <summary> 140 /// 加入下载任务组 141 /// </summary> 142 /// <param name="multipleDownloadTask"></param> 143 public void Add(DownloadTaskGroup downloadTaskGroup) 144 { 145 lock (locker) 146 { 147 foreach (var downloadTask in downloadTaskGroup.Children) 148 { 149 Add(downloadTask); 150 } 151 downloadTaskGroup.DownloadEngine = this; 152 } 153 } 154 155 /// <summary> 156 /// 移除下载任务 157 /// </summary> 158 /// <param name="downloadTask"></param> 159 public void Remove(DownloadTask downloadTask) 160 { 161 DownloadClientBase downloadClient = null; 162 163 lock (locker) 164 { 165 if (_downloading.TryGetValue(downloadTask, out downloadClient)) 166 _downloading.Remove(downloadTask); 167 else if (_waiting.Remove(downloadTask)) 168 { 169 var groupStatus = downloadTask.Group?.DownloadStatus; 170 171 downloadTask.DownloadStatus = DownloadStatus.Cancelled; 172 downloadTask.DownloadFileCompleted?.Invoke(downloadTask); 173 174 if (downloadTask.Group != null) 175 downloadTask.Group.DownloadFileCompleted?.Invoke(downloadTask.Group); 176 } 177 } 178 179 // 取消下载 180 downloadClient?.BeginCancel(); 181 } 182 183 /// <summary> 184 /// 移除指定下载任务 185 /// </summary> 186 /// <param name="predicate"></param> 187 public void Remove(Func<DownloadTask, bool> predicate) 188 { 189 lock (locker) 190 { 191 foreach (var item in _waiting.Where(predicate).ToArray()) 192 { 193 Remove(item); 194 } 195 196 foreach (var item in _downloading.Keys.Where(predicate).ToArray()) 197 { 198 Remove(item); 199 } 200 } 201 } 202 203 /// <summary> 204 /// 移除指定下载任务组 205 /// </summary> 206 /// <param name="predicate"></param> 207 public void Remove(Func<DownloadTaskGroup, bool> predicate) 208 { 209 lock (locker) 210 { 211 foreach (var item in _waiting.Where(q => q.Group != null && predicate.Invoke(q.Group)).ToArray()) 212 { 213 Remove(item); 214 } 215 216 foreach (var item in _downloading.Keys.Where(q => q.Group != null && predicate.Invoke(q.Group)).ToArray()) 217 { 218 Remove(item); 219 } 220 } 221 } 222 223 /// <summary> 224 /// 移除下载任务组 225 /// </summary> 226 /// <param name="downloadTaskGroup"></param> 227 public void Remove(DownloadTaskGroup downloadTaskGroup) 228 { 229 lock (locker) 230 { 231 foreach (var downloadTask in downloadTaskGroup.Children) 232 { 233 Remove(downloadTask); 234 } 235 } 236 } 237 238 /// <summary> 239 /// 移除等待中的下载任务,并且不触发事件 240 /// </summary> 241 /// <param name="downloadTask"></param> 242 public bool RemoveWaitingTask(DownloadTask downloadTask) 243 { 244 lock (locker) 245 { 246 if (_waiting.Remove(downloadTask)) 247 { 248 downloadTask.DownloadStatus = DownloadStatus.Cancelled; 249 250 if (downloadTask.Group != null) 251 { 252 var group = downloadTask.Group; 253 254 downloadTask.Group = null; 255 group.Children.Remove(downloadTask); 256 } 257 258 downloadTask.Removed?.Invoke(downloadTask); 259 260 return true; 261 } 262 } 263 264 return false; 265 } 266 267 /// <summary> 268 /// 插入下载任务 269 /// </summary> 270 /// <param name="downloadTask"></param> 271 public void Unshift(DownloadTask downloadTask) 272 { 273 lock (locker) 274 { 275 _waiting.Insert(0, downloadTask); 276 downloadTask.DownloadEngine = this; 277 278 if (downloadTask.Group != null) 279 { 280 downloadTask.Group.Children.Add(downloadTask); 281 downloadTask.Group.Appended?.Invoke(downloadTask); 282 } 283 } 284 } 285 286 /// <summary> 287 /// 插入下载任务 288 /// </summary> 289 public void UnshiftBefore(DownloadTask newDownloadTask, DownloadTask beforeDownloadTask) 290 { 291 lock (locker) 292 { 293 var index = _waiting.IndexOf(beforeDownloadTask); 294 _waiting.Insert(index, newDownloadTask); 295 newDownloadTask.DownloadEngine = this; 296 297 if (newDownloadTask.Group != null) 298 { 299 index = newDownloadTask.Group.Children.IndexOf(beforeDownloadTask); 300 if (index == -1) 301 { 302 newDownloadTask.Group.Children.Add(newDownloadTask); 303 } 304 else 305 { 306 newDownloadTask.Group.Children.Insert(index, newDownloadTask); 307 } 308 newDownloadTask.Group.Appended?.Invoke(newDownloadTask); 309 } 310 } 311 } 312 313 /// <summary> 314 /// 获取下载任务 315 /// </summary> 316 /// <param name="status">下载状态 0:全部|1:等待下载|2:下载中</param> 317 /// <returns></returns> 318 public DownloadTask[] GetDownloadTasks(int status = 0) 319 { 320 lock (locker) 321 { 322 switch (status) 323 { 324 case 1: 325 return _waiting.ToArray(); 326 case 2: 327 return _downloading.Keys.ToArray(); 328 default: 329 var list = new List<DownloadTask>(_waiting.Count + _downloading.Count); 330 list.AddRange(_waiting); 331 list.AddRange(_downloading.Keys); 332 return list.ToArray(); 333 } 334 } 335 } 336 337 /// <summary> 338 /// 获取下载任务组 339 /// </summary> 340 /// <param name="status">下载状态 0:全部|1:等待下载|2:下载中</param> 341 /// <returns></returns> 342 public DownloadTaskGroup[] GetDownloadTaskGroups(int status = 0) 343 { 344 lock (locker) 345 { 346 var list = new List<DownloadTaskGroup>(); 347 list.AddRange(_waiting.Where(q => q.Group != null).Select(q => q.Group)); 348 list.AddRange(_downloading.Keys.Where(q => q.Group != null).Select(q => q.Group)); 349 var query = list.Distinct(); 350 351 switch (status) 352 { 353 case 1: 354 return query.Where(q => q.DownloadStatus == DownloadStatus.Waiting).ToArray(); 355 case 2: 356 return query.Where(q => q.DownloadStatus == DownloadStatus.Downloading).ToArray(); 357 default: 358 return query.ToArray(); 359 } 360 } 361 } 362 363 public bool AnyTask(Func<DownloadTask, bool> predicate = null) 364 { 365 return (predicate == null ? _waiting.Any() : _waiting.Any(predicate)) || (predicate == null ? _downloading.Any() : _downloading.Keys.Any(predicate)); 366 } 367 368 public Task<bool> AnyTaskGroupAsync(Func<DownloadTaskGroup, bool> predicate) 369 { 370 return Task.Run(() => AnyTaskGroup(predicate)); 371 } 372 373 public bool AnyTaskGroup(Func<DownloadTaskGroup, bool> predicate) 374 { 375 return _waiting.Where(q => q.Group != null).Select(q => q.Group).Any(predicate) || _downloading.Keys.Where(q => q.Group != null).Select(q => q.Group).Any(predicate); 376 } 377 378 public DownloadTask GetDownloadTask(Func<DownloadTask, bool> predicate) 379 { 380 lock (locker) 381 { 382 return _waiting.FirstOrDefault(predicate) ?? _downloading.Keys.FirstOrDefault(predicate); 383 } 384 } 385 386 public DownloadTaskGroup GetDownloadTaskGroup(Func<DownloadTaskGroup, bool> predicate) 387 { 388 lock (locker) 389 { 390 return _waiting.Where(q => q.Group != null).Select(q => q.Group).FirstOrDefault(predicate) ?? _downloading.Keys.Where(q => q.Group != null).Select(q => q.Group).FirstOrDefault(predicate); 391 } 392 } 393 394 /// <summary> 395 /// 查询第一个符合描述的下载任务 396 /// </summary> 397 /// <param name="predicate"></param> 398 /// <returns></returns> 399 public DownloadTask GetOrAdd(Func<DownloadTask, bool> predicate, Func<DownloadTask> create) 400 { 401 lock (locker) 402 { 403 var task = _waiting.FirstOrDefault(predicate); 404 405 if (task != null) 406 return task; 407 408 task = _downloading.Keys.FirstOrDefault(predicate); 409 410 if (task != null) 411 return task; 412 413 task = create.Invoke(); 414 415 if (task != null) 416 Add(task); 417 418 return task; 419 } 420 } 421 422 /// <summary> 423 /// 查询第一个符合描述的下载任务组 424 /// </summary> 425 /// <param name="predicate"></param> 426 /// <returns></returns> 427 public DownloadTaskGroup GetOrAdd(Func<DownloadTaskGroup, bool> predicate, Func<DownloadTaskGroup> create) 428 { 429 lock (locker) 430 { 431 var group = _waiting.Where(q => q.Group != null).Select(q => q.Group).FirstOrDefault(predicate); 432 433 if (group != null) 434 return group; 435 436 group = _downloading.Keys.Where(q => q.Group != null).Select(q => q.Group).FirstOrDefault(predicate); 437 438 if (group != null) 439 return group; 440 441 group = create.Invoke(); 442 443 if (group != null) 444 Add(group); 445 446 return group; 447 } 448 } 449 450 /// <summary> 451 /// 开始轮询等待下载队列 452 /// </summary> 453 private void Run() 454 { 455 try 456 { 457 ContainerLocator.Current.Resolve<IEventAggregator>().GetEvent<IsIdleEvent>().Publish((this, false)); 458 459 while (true) 460 { 461 var ret = _semaphore.WaitOne(10000); 462 463 lock (locker) 464 { 465 if (!ret) 466 { 467 if (_downloading.Count < _maximumCount) 468 { 469 // 到这里一定是代码出错了 470 Log.Debug($"等待信号失败,强制释放{_maximumCount - _downloading.Count}信号量"); 471 472 try 473 { 474 TryRelease(_maximumCount - _downloading.Count); 475 } 476 catch { } 477 } 478 else if (_waiting.Count == 0 && _downloading.Count == 0) 479 { 480 _thread = null; 481 break; 482 } 483 484 continue; 485 } 486 487 if (_waiting.Count == 0 && _downloading.Count == 0) 488 { 489 _thread = null; 490 TryRelease(); // 耗费了一个信号,要释放一个 491 break; 492 } 493 494 if (_waiting.Count > 0) 495 { 496 var downloadTask = _waiting.First(); 497 _waiting.Remove(downloadTask); 498 _downloading.Add(downloadTask, null); 499 500 ThreadPool.QueueUserWorkItem(Start, downloadTask); 501 } 502 } 503 } 504 } 505 finally 506 { 507 ContainerLocator.Current.Resolve<IEventAggregator>().GetEvent<IsIdleEvent>().Publish((this, true)); 508 } 509 } 510 511 /// <summary> 512 /// 开始新的下载任务 513 /// </summary> 514 /// <param name="state"></param> 515 private void Start(object state) 516 { 517 var downloadTask = (DownloadTask)state; 518 DownloadClientBase downloadClient = null; 519 520 try 521 { 522 if (downloadTask.DownloadStatus == DownloadStatus.Completed) 523 { 524 // 跳过,不触发事件 525 lock (locker) 526 { 527 _downloading.Remove(downloadTask); 528 529 if (_downloading.Count < _maximumCount) 530 TryRelease(_maximumCount - _downloading.Count); 531 } 532 return; 533 } 534 535 var groupStatus = downloadTask.Group == null ? DownloadStatus.Waiting : downloadTask.Group.DownloadStatus; 536 downloadTask.DownloadStatus = DownloadStatus.Downloading; 537 downloadTask.BeginTime = DateTime.Now; 538 539 Log.Info($"开始下载,URL:{downloadTask.Url}"); 540 541 if (downloadTask.PreviewDownload != null) 542 { 543 var ret = downloadTask.PreviewDownload.Invoke(downloadTask); 544 545 if (!ret.IsSucc) 546 throw ret.Error ?? new Exception(); 547 548 if (downloadTask.DownloadStatus == DownloadStatus.Completed) 549 { 550 downloadTask.DownloadFileCompleted?.Invoke(downloadTask); 551 552 lock (locker) 553 { 554 _downloading.Remove(downloadTask); 555 556 if (_downloading.Count < _maximumCount) 557 TryRelease(_maximumCount - _downloading.Count); 558 } 559 560 return; 561 } 562 } 563 564 // 判断是否下载任务组 565 if (downloadTask.Group != null) 566 { 567 var total = downloadTask.Group.TotalBytesToReceive; 568 569 // 计算总字节数 570 foreach (var child in downloadTask.Group.Children) 571 { 572 if (child.DownloadStatus != DownloadStatus.Waiting || child.TotalBytesToReceive != 0) 573 continue; 574 575 var child_dc = child.CreateDownloadClient?.Invoke() ?? new HttpDownloadClient(child); 576 var ret = child_dc.GetFileSize(); 577 578 if (!ret.IsSucc) 579 { 580 child.Error = ret.Error; 581 if (ret.Error != null) 582 { 583 var msg = ErrorTexts.HttpError(ret.Error, "下载文件"); 584 child.ErrorMsg = msg; 585 } 586 child.DownloadStatus = DownloadStatus.Error; 587 child.DownloadFileCompleted?.Invoke(child); 588 // 任意一个获取失败,则取消整组下载 589 throw ret.Error ?? new Exception($"访问下载资源失败,Url:{child.Url}"); 590 } 591 592 child.TotalBytesToReceive = ret.Value; 593 child.DownloadProgressChanged?.Invoke(downloadTask); 594 } 595 596 if (downloadTask.Group.TotalBytesToReceive != total) 597 downloadTask.Group.DownloadProgressChanged?.Invoke(downloadTask.Group); 598 599 if (downloadTask.Group.PreviewDownload != null) 600 { 601 var ret = downloadTask.Group.PreviewDownload.Invoke(downloadTask.Group); 602 603 if (!ret.IsSucc) 604 throw ret.Error ?? new Exception(); 605 if (downloadTask.Group.DownloadStatus == DownloadStatus.Completed) 606 { 607 downloadTask.Group.DownloadFileCompleted?.Invoke(downloadTask.Group); 608 609 lock (locker) 610 { 611 Remove(downloadTask.Group); 612 613 if (_downloading.Count < _maximumCount) 614 TryRelease(_maximumCount - _downloading.Count); 615 } 616 617 return; 618 } 619 } 620 } 621 622 lock (locker) 623 { 624 if (!_downloading.ContainsKey(downloadTask)) 625 { 626 throw new TaskCanceledException(); 627 } 628 629 downloadClient = downloadTask.CreateDownloadClient?.Invoke() ?? new HttpDownloadClient(downloadTask); 630 downloadClient.DownloadProgressChanged -= OnDownloadProgressChanged; 631 downloadClient.DownloadProgressChanged += OnDownloadProgressChanged; 632 downloadClient.DownloadFileCompleted -= OnDownloadFileCompleted; 633 downloadClient.DownloadFileCompleted += OnDownloadFileCompleted; 634 635 _downloading[downloadTask] = downloadClient; 636 } 637 638 downloadTask.DownloadStatus = DownloadStatus.Downloading; 639 downloadClient.BeginDownload(); 640 } 641 catch (Exception ex) 642 { 643 Log.Info($"下载文件失败\tUrl:{downloadTask.Url}\tMsg:{ex.Message}"); 644 645 if (ex is TaskCanceledException) 646 { 647 downloadTask.DownloadStatus = DownloadStatus.Cancelled; 648 } 649 else 650 { 651 downloadTask.ErrorMsg = ErrorTexts.HttpError(ex, "下载文件"); 652 Log.Error(downloadTask.Error = ex); 653 downloadTask.DownloadStatus = DownloadStatus.Error; 654 } 655 656 downloadTask.DownloadFileCompleted?.Invoke(downloadTask); 657 658 lock (locker) 659 { 660 if (downloadTask.Group != null) 661 { 662 downloadTask.Group.DownloadFileCompleted?.Invoke(downloadTask.Group); 663 Remove(downloadTask.Group); 664 } 665 else 666 _downloading.Remove(downloadTask); 667 668 if (_downloading.Count < _maximumCount) 669 TryRelease(_maximumCount - _downloading.Count); 670 } 671 672 downloadClient?.Dispose(); 673 } 674 } 675 676 /// <summary> 677 /// 下载进度改变或下载完成 678 /// </summary> 679 private void OnDownloadProgressChanged((DownloadClientBase sender, long BytesReceived, long TotalBytesToReceive, int ProgressPercentage) e) 680 { 681 var downloadClient = e.sender; 682 var downloadTask = downloadClient.DownloadTask; 683 684 downloadTask.ProgressPercentage = e.ProgressPercentage; 685 downloadTask.TotalBytesToReceive = e.TotalBytesToReceive; 686 downloadTask.BytesReceived = e.BytesReceived; 687 688 if (downloadTask.DownloadStatus == DownloadStatus.Waiting) 689 downloadTask.DownloadStatus = DownloadStatus.Downloading; 690 691 downloadTask.DownloadProgressChanged?.Invoke(downloadTask); 692 downloadTask.Group?.DownloadProgressChanged?.Invoke(downloadTask.Group); 693 } 694 695 /// <summary> 696 /// 下载完成或下载失败或下载取消 697 /// </summary> 698 private void OnDownloadFileCompleted((DownloadClientBase sender, Exception Error, bool Cancelled) e) 699 { 700 var downloadClient = e.sender; 701 var downloadTask = downloadClient.DownloadTask; 702 703 downloadClient.DownloadProgressChanged -= OnDownloadProgressChanged; 704 downloadClient.DownloadFileCompleted -= OnDownloadFileCompleted; 705 706 var groupStatus = downloadTask.Group?.DownloadStatus; 707 708 try 709 { 710 downloadTask.EndTime = DateTime.Now; 711 712 if (e.Cancelled) 713 { 714 downloadTask.DownloadStatus = DownloadStatus.Cancelled; 715 716 // 取消整组 717 if (downloadTask.Group != null) 718 { 719 Remove(downloadTask.Group); 720 downloadTask.Group.DownloadFileCompleted?.Invoke(downloadTask.Group); 721 } 722 } 723 else if (e.Error != null) 724 { 725 downloadTask.ErrorMsg = ErrorTexts.HttpError(e.Error, "下载文件"); 726 downloadTask.Error = e.Error; 727 downloadTask.DownloadStatus = DownloadStatus.Error; 728 729 // 取消整组 730 if (downloadTask.Group != null) 731 { 732 Remove(downloadTask.Group); 733 downloadTask.Group.DownloadFileCompleted?.Invoke(downloadTask.Group); 734 } 735 } 736 else if (downloadTask.DownloadStatus != DownloadStatus.Completed) 737 { 738 if (downloadTask.TotalBytesToReceive == 0 || downloadTask.TotalBytesToReceive != downloadTask.BytesReceived) 739 { 740 if (downloadTask.TotalBytesToReceive == 0) 741 { 742 var msg = ErrorTexts.Get(ErrorTextType.ConnectFailed, "下载文件"); 743 downloadTask.ErrorMsg = msg; 744 downloadTask.Error = new WebException(msg, WebExceptionStatus.ConnectFailure); 745 } 746 else if (downloadTask.TotalBytesToReceive != downloadTask.BytesReceived) 747 { 748 var msg = ErrorTexts.Get(ErrorTextType.NetworkDisconnect); 749 downloadTask.ErrorMsg = msg; 750 downloadTask.Error = new WebException(msg, WebExceptionStatus.ConnectionClosed); 751 } 752 753 downloadTask.DownloadStatus = DownloadStatus.Error; 754 755 // 取消整组 756 if (downloadTask.Group != null) 757 { 758 Remove(downloadTask.Group); 759 downloadTask.Group.DownloadFileCompleted?.Invoke(downloadTask.Group); 760 } 761 } 762 else 763 downloadTask.DownloadStatus = DownloadStatus.Completed; 764 } 765 766 downloadTask.DownloadFileCompleted?.Invoke(downloadTask); 767 768 if (downloadTask.Group != null && downloadTask.Group.DownloadFileCompleted != null && downloadTask.Group.DownloadStatus == DownloadStatus.Completed) 769 downloadTask.Group.DownloadFileCompleted.Invoke(downloadTask.Group); 770 771 lock (locker) 772 { 773 _downloading.Remove(downloadTask); 774 775 if (_downloading.Count < _maximumCount) 776 TryRelease(_maximumCount - _downloading.Count); 777 } 778 } 779 catch (Exception ex) 780 { 781 try 782 { 783 downloadTask.ErrorMsg = ErrorTexts.HttpError(ex, "下载文件"); 784 downloadTask.Error = ex; 785 downloadTask.DownloadStatus = DownloadStatus.Error; 786 downloadTask.DownloadFileCompleted?.Invoke(downloadTask); 787 788 if (downloadTask.Group != null && downloadTask.Group.DownloadFileCompleted != null) 789 downloadTask.Group.DownloadFileCompleted.Invoke(downloadTask.Group); 790 791 lock (locker) 792 { 793 _downloading.Remove(downloadTask); 794 795 if (_downloading.Count < _maximumCount) 796 TryRelease(_maximumCount - _downloading.Count); 797 798 if (downloadTask.Group != null) 799 { 800 downloadTask.Group.DownloadFileCompleted?.Invoke(downloadTask.Group); 801 Remove(downloadTask.Group); 802 } 803 } 804 } 805 catch { } 806 } 807 finally 808 { 809 downloadClient.Dispose(); 810 811 if (downloadTask.Error != null) 812 Log.Error(downloadTask.Error, $"下载文件失败,Url:{downloadTask.Url}"); 813 } 814 } 815 816 private void TryRelease(int count = 1) 817 { 818 if (_semaphore == null) 819 return; 820 821 try 822 { 823 while (count-- > 0) 824 { 825 _semaphore.Release(); 826 } 827 } 828 catch { } 829 } 830 831 public void GetCount(out int waitingCount, out int downloadingCount) 832 { 833 lock (locker) 834 { 835 waitingCount = _waiting.Count; 836 downloadingCount = _downloading.Count; 837 } 838 } 839 840 #endregion 841 842 #region Property 843 844 /// <summary> 845 /// 下载器名称 846 /// </summary> 847 public string Name { get; } 848 849 #endregion 850 851 #region Field 852 853 private object locker = new object(); 854 /// <summary> 855 /// 信号量,限制同时下载数 856 /// </summary> 857 private Semaphore _semaphore; 858 private int _maximumCount; 859 /// <summary> 860 /// 等待下载队列 861 /// </summary> 862 private List<DownloadTask> _waiting = new List<DownloadTask>(); 863 /// <summary> 864 /// 正在下载队列 865 /// </summary> 866 private Dictionary<DownloadTask, DownloadClientBase> _downloading = new Dictionary<DownloadTask, DownloadClientBase>(); 867 private Thread _thread; 868 869 #endregion 870 } 871 872 /// <summary> 873 /// 下载任务 874 /// </summary> 875 public sealed class DownloadTask : BindableBase 876 { 877 private SpeedCalculator _speedCalculator; 878 879 /// <summary> 880 /// 下载任务 881 /// </summary> 882 /// <param name="downloadSpeedEnable">是否计算下载速度</param> 883 public DownloadTask(bool downloadSpeedEnable = false) 884 { 885 if (downloadSpeedEnable) 886 { 887 DownloadProgressChanged += OnDownloadProgressChangedForDownloadSpeed; 888 DownloadFileCompleted += OnDownloadFileCompletedForDownloadSpeed; 889 } 890 } 891 892 /// <summary> 893 /// 下载名称 894 /// </summary> 895 public string Name { get; set; } 896 /// <summary> 897 /// 下载编码 898 /// </summary> 899 public string Code { get; set; } 900 /// <summary> 901 /// 额外信息 902 /// </summary> 903 public object Extra { get; set; } 904 /// <summary> 905 /// 额外信息 906 /// </summary> 907 public Dictionary<String, String> Extras { get; set; } 908 /// <summary> 909 /// 下载链接 910 /// </summary> 911 public string Url { get; set; } 912 /// <summary> 913 /// 远程主机 914 /// </summary> 915 public string Host { get; set; } 916 /// <summary> 917 /// 端口号 918 /// </summary> 919 public int Port { get; set; } 920 /// <summary> 921 /// 用户名 922 /// </summary> 923 public string UserName { get; set; } 924 /// <summary> 925 /// 密码 926 /// </summary> 927 public string Password { get; set; } 928 /// <summary> 929 /// 保存路径 930 /// </summary> 931 public string FilePath { get; set; } 932 /// <summary> 933 /// 请求头 934 /// </summary> 935 public Dictionary<String, String> Headers { get; set; } 936 937 /// <summary> 938 /// 下载进度改变或下载完成 939 /// </summary> 940 public Action<DownloadTask> DownloadProgressChanged { get; set; } 941 942 /// <summary> 943 /// 进度百分比 944 /// </summary> 945 public int ProgressPercentage { get; set; } 946 /// <summary> 947 /// 总字节数 948 /// </summary> 949 public long TotalBytesToReceive { get; set; } 950 /// <summary> 951 /// 已接收字节数 952 /// </summary> 953 public long BytesReceived { get; set; } 954 /// <summary> 955 /// 下载速度 B/s 956 /// </summary> 957 public long? DownloadSpeed { get; private set; } 958 /// <summary> 959 /// 不显示下载速度 960 /// </summary> 961 public bool NoSpeed { get; set; } 962 /// <summary> 963 /// 固定百分比 964 /// </summary> 965 public double FixedPercentage { get; set; } 966 /// <summary> 967 /// 剩余时间(秒) 968 /// </summary> 969 public Double TimeRemaining { get; set; } 970 971 /// <summary> 972 /// 下载完成或下载失败或下载取消 973 /// </summary> 974 public Action<DownloadTask> DownloadFileCompleted { get; set; } 975 /// <summary> 976 /// 不触发事件的取消下载 977 /// </summary> 978 public Action<DownloadTask> Removed { get; set; } 979 980 /// <summary> 981 /// 是否已取消 982 /// </summary> 983 public bool Cancelled { get; set; } 984 /// <summary> 985 /// 发生的异常 986 /// </summary> 987 public Exception Error { get; set; } 988 /// <summary> 989 /// 错误消息 990 /// </summary> 991 public string ErrorMsg { get; set; } 992 993 /// <summary> 994 /// 下载状态 995 /// </summary> 996 public DownloadStatus DownloadStatus { get; set; } 997 998 /// <summary> 999 /// 下载前触发 1000 /// </summary> 1001 public Func<DownloadTask, ResultValue> PreviewDownload { get; set; } 1002 1003 /// <summary> 1004 /// 所在下载任务组 1005 /// </summary> 1006 public DownloadTaskGroup Group { get; set; } 1007 1008 /// <summary> 1009 /// 下载器 1010 /// </summary> 1011 public DownloadEngine DownloadEngine { get; set; } 1012 1013 /// <summary> 1014 /// 自定义下载方式 1015 /// </summary> 1016 public Func<DownloadClientBase> CreateDownloadClient { get; set; } 1017 1018 public Action<object> Retrying { get; set; } 1019 1020 /// <summary> 1021 /// 开始时间 1022 /// </summary> 1023 public DateTime? BeginTime { get; set; } 1024 /// <summary> 1025 /// 结束时间 1026 /// </summary> 1027 public DateTime? EndTime { get; set; } 1028 /// <summary> 1029 /// 下载耗时 1030 /// </summary> 1031 public TimeSpan? Duration => EndTime - BeginTime; 1032 /// <summary> 1033 /// 平均下载速度 1034 /// </summary> 1035 public long? AverageDownloadSpeed => EndTime.HasValue ? ((long?)(BytesReceived / Duration?.TotalSeconds)) : null; 1036 1037 1038 private void OnDownloadProgressChangedForDownloadSpeed(DownloadTask obj) 1039 { 1040 if (DownloadStatus == DownloadStatus.Downloading && _speedCalculator == null && !NoSpeed) 1041 { 1042 _speedCalculator = new SpeedCalculator(() => BytesReceived, n => 1043 { 1044 if (DownloadStatus != DownloadStatus.Downloading) 1045 { 1046 if (_speedCalculator != null) 1047 { 1048 _speedCalculator.Dispose(); 1049 _speedCalculator = null; 1050 } 1051 } 1052 1053 DownloadSpeed = n; 1054 if (n == 0) 1055 { 1056 TimeRemaining = Double.NaN; 1057 } 1058 else 1059 { 1060 var bytes_remaining = TotalBytesToReceive - BytesReceived; 1061 TimeRemaining = bytes_remaining / n; 1062 } 1063 DownloadProgressChanged?.Invoke(this); 1064 }); 1065 _speedCalculator.Start(BytesReceived); 1066 } 1067 1068 RaisePropertyChanged(null); 1069 } 1070 1071 private void OnDownloadFileCompletedForDownloadSpeed(DownloadTask obj) 1072 { 1073 if (_speedCalculator != null) 1074 { 1075 _speedCalculator.Dispose(); 1076 _speedCalculator = null; 1077 } 1078 1079 RaisePropertyChanged(null); 1080 } 1081 1082 public void Retry() 1083 { 1084 DownloadStatus = DownloadStatus.Waiting; 1085 BytesReceived = 0; 1086 TotalBytesToReceive = 0; 1087 ProgressPercentage = 0; 1088 Error = null; 1089 ErrorMsg = null; 1090 Cancelled = false; 1091 1092 Retrying?.Invoke(this); 1093 RaisePropertyChanged(null); 1094 DownloadEngine.Add(this); 1095 } 1096 1097 public override string ToString() 1098 { 1099 var sb = new StringBuilder(nameof(DownloadTask)); 1100 1101 if (!String.IsNullOrEmpty(Name)) 1102 { 1103 sb.Append(", name: " + Name); 1104 } 1105 if (!String.IsNullOrEmpty(Url)) 1106 { 1107 sb.Append(", url: " + Url); 1108 } 1109 if (!String.IsNullOrEmpty(Host)) 1110 { 1111 sb.Append(", host: " + Host); 1112 } 1113 if (Port != 0) 1114 { 1115 sb.Append(", port: " + Port); 1116 } 1117 if (!String.IsNullOrEmpty(UserName)) 1118 { 1119 sb.Append(", userName: " + UserName); 1120 } 1121 if (!String.IsNullOrEmpty(Password)) 1122 { 1123 sb.Append(", password: " + Password); 1124 } 1125 if (!String.IsNullOrEmpty(FilePath)) 1126 { 1127 sb.Append(", filePath: " + FilePath); 1128 } 1129 sb.Append($", downloadStatus: {DownloadStatus}。"); 1130 1131 return sb.ToString(); 1132 } 1133 } 1134 1135 /// <summary> 1136 /// 下载任务组 1137 /// </summary> 1138 public sealed class DownloadTaskGroup : BindableBase 1139 { 1140 private SpeedCalculator _speedCalculator; 1141 1142 /// <summary> 1143 /// 下载任务组 1144 /// </summary> 1145 /// <param name="downloadTasks">子下载任务</param> 1146 public DownloadTaskGroup(IEnumerable<DownloadTask> downloadTasks, bool downloadSpeedEnable = false) 1147 { 1148 Children = new List<DownloadTask>(downloadTasks); 1149 1150 foreach (var downloadTask in downloadTasks) 1151 { 1152 downloadTask.Group = this; 1153 } 1154 1155 if (downloadSpeedEnable) 1156 { 1157 DownloadProgressChanged += OnDownloadProgressChangedForDownloadSpeed; 1158 DownloadFileCompleted += OnDownloadFileCompletedForDownloadSpeed; 1159 } 1160 } 1161 1162 /// <summary> 1163 /// 子下载任务 1164 /// </summary> 1165 public List<DownloadTask> Children { get; } 1166 /// <summary> 1167 /// 追加子任务 1168 /// </summary> 1169 public Action<DownloadTask> Appended { get; set; } 1170 1171 /// <summary> 1172 /// 下载名称 1173 /// </summary> 1174 public string Name { get; set; } 1175 /// <summary> 1176 /// 下载编码 1177 /// </summary> 1178 public string Code { get; set; } 1179 /// <summary> 1180 /// 额外信息 1181 /// </summary> 1182 public object Extra { get; set; } 1183 1184 /// <summary> 1185 /// 下载进度改变或下载完成 1186 /// </summary> 1187 public Action<DownloadTaskGroup> DownloadProgressChanged { get; set; } 1188 1189 /// <summary> 1190 /// 进度百分比 1191 /// </summary> 1192 public int ProgressPercentage 1193 { 1194 get 1195 { 1196 var fixedSum = Children.Sum(q => q.FixedPercentage) / 100; 1197 var totalBytes = (long)(TotalBytesToReceive / (1 - fixedSum)); 1198 1199 var received = 0d; 1200 1201 foreach (var child in Children) 1202 { 1203 if (child.FixedPercentage == 0) 1204 { 1205 received += child.BytesReceived; 1206 } 1207 else 1208 { 1209 switch (child.DownloadStatus) 1210 { 1211 case DownloadStatus.Completed: 1212 received += totalBytes * child.FixedPercentage / 100; 1213 break; 1214 case DownloadStatus.Downloading: 1215 received += totalBytes * child.FixedPercentage / 100 * child.ProgressPercentage / 100; 1216 break; 1217 } 1218 } 1219 } 1220 1221 return totalBytes == 0 ? 0 : (int)(received * 100 / totalBytes); 1222 } 1223 } 1224 /// <summary> 1225 /// 总字节数 1226 /// </summary> 1227 public long TotalBytesToReceive => Children.Where(q => q.FixedPercentage == 0).Sum(q => q.TotalBytesToReceive); 1228 /// <summary> 1229 /// 已接收字节数 1230 /// </summary> 1231 public long BytesReceived => Children.Where(q => q.FixedPercentage == 0).Sum(q => q.BytesReceived); 1232 /// <summary> 1233 /// 下载速度 B/s 1234 /// </summary> 1235 public long? DownloadSpeed { get; private set; } 1236 /// <summary> 1237 /// 不显示下载速度 1238 /// </summary> 1239 public bool NoSpeed => CurrentDownloadTask.NoSpeed; 1240 /// <summary> 1241 /// 剩余时间(秒) 1242 /// </summary> 1243 public Double TimeRemaining { get; set; } 1244 1245 /// <summary> 1246 /// 下载完成或下载失败或下载取消 1247 /// </summary> 1248 public Action<DownloadTaskGroup> DownloadFileCompleted { get; set; } 1249 1250 /// <summary> 1251 /// 发生的异常 1252 /// </summary> 1253 public Exception Error => Children.FirstOrDefault(q => q.Error != null)?.Error; 1254 /// <summary> 1255 /// 错误消息 1256 /// </summary> 1257 public string ErrorMsg => Children.FirstOrDefault(q => q.Error != null)?.ErrorMsg; 1258 1259 /// <summary> 1260 /// 下载状态 1261 /// </summary> 1262 public DownloadStatus DownloadStatus 1263 { 1264 get 1265 { 1266 if (Children.Any(q => q.DownloadStatus == DownloadStatus.Error)) 1267 return DownloadStatus.Error; 1268 if (Children.Any(q => q.DownloadStatus == DownloadStatus.Cancelled)) 1269 return DownloadStatus.Cancelled; 1270 if (Children.Any(q => q.DownloadStatus == DownloadStatus.Downloading)) 1271 return DownloadStatus.Downloading; 1272 if (Children.All(q => q.DownloadStatus == DownloadStatus.Waiting)) 1273 return DownloadStatus.Waiting; 1274 if (Children.Any(q => q.DownloadStatus == DownloadStatus.Waiting)) 1275 return DownloadStatus.Downloading; 1276 return DownloadStatus.Completed; 1277 } 1278 } 1279 1280 public DownloadTask CurrentDownloadTask 1281 { 1282 get 1283 { 1284 while (Children.Count > 0) 1285 { 1286 var downloadTask = GetDownloadTask(); 1287 if (downloadTask != null) 1288 { 1289 return downloadTask; 1290 } 1291 Thread.Yield(); 1292 } 1293 return null; 1294 } 1295 } 1296 private DownloadTask GetDownloadTask() 1297 { 1298 var status = DownloadStatus; 1299 1300 if (status == DownloadStatus.Waiting) 1301 return Children.FirstOrDefault(q => q.DownloadStatus == status); 1302 else if (status == DownloadStatus.Downloading) 1303 return Children.FirstOrDefault(q => q.DownloadStatus == status) ?? Children.FirstOrDefault(q => q.DownloadStatus == DownloadStatus.Waiting); 1304 else 1305 return Children.LastOrDefault(q => q.DownloadStatus == status) ?? Children.LastOrDefault(); 1306 } 1307 1308 /// <summary> 1309 /// 下载器 1310 /// </summary> 1311 public DownloadEngine DownloadEngine { get; set; } 1312 1313 /// <summary> 1314 /// 下载前触发 1315 /// </summary> 1316 public Func<DownloadTaskGroup, ResultValue> PreviewDownload { get; set; } 1317 public Action<object> Retrying { get; set; } 1318 1319 /// <summary> 1320 /// 开始时间 1321 /// </summary> 1322 public DateTime? BeginTime => Children.Where(q => q.BeginTime.HasValue).Min(q => q.BeginTime); 1323 /// <summary> 1324 /// 结束时间 1325 /// </summary> 1326 public DateTime? EndTime => Children.Where(q => q.EndTime.HasValue || q.BeginTime.HasValue).Max(q => q.EndTime.HasValue ? q.EndTime : q.BeginTime); 1327 /// <summary> 1328 /// 下载耗时 1329 /// </summary> 1330 public TimeSpan? Duration => EndTime - BeginTime; 1331 /// <summary> 1332 /// 平均下载速度 1333 /// </summary> 1334 public long? AverageDownloadSpeed => EndTime.HasValue ? ((long?)(BytesReceived / Duration?.TotalSeconds)) : null; 1335 1336 private void OnDownloadProgressChangedForDownloadSpeed(DownloadTaskGroup obj) 1337 { 1338 if (DownloadStatus == DownloadStatus.Downloading && _speedCalculator == null) 1339 { 1340 _speedCalculator = new SpeedCalculator(() => BytesReceived, n => 1341 { 1342 if (DownloadStatus != DownloadStatus.Downloading) 1343 { 1344 if (_speedCalculator != null) 1345 { 1346 _speedCalculator.Dispose(); 1347 _speedCalculator = null; 1348 } 1349 } 1350 if (NoSpeed) 1351 { 1352 DownloadSpeed = null; 1353 TimeRemaining = Double.NaN; 1354 } 1355 else 1356 { 1357 DownloadSpeed = n; 1358 if (n == 0) 1359 { 1360 TimeRemaining = Double.NaN; 1361 } 1362 else 1363 { 1364 var bytes_remaining = TotalBytesToReceive - BytesReceived; 1365 TimeRemaining = bytes_remaining / n; 1366 } 1367 } 1368 DownloadProgressChanged?.Invoke(this); 1369 }); 1370 _speedCalculator.Start(BytesReceived); 1371 } 1372 1373 RaisePropertyChanged(null); 1374 } 1375 1376 private void OnDownloadFileCompletedForDownloadSpeed(DownloadTaskGroup obj) 1377 { 1378 if (_speedCalculator != null) 1379 { 1380 _speedCalculator.Dispose(); 1381 _speedCalculator = null; 1382 } 1383 1384 RaisePropertyChanged(null); 1385 } 1386 1387 public void Retry() 1388 { 1389 foreach (var child in Children) 1390 { 1391 child.DownloadStatus = DownloadStatus.Waiting; 1392 child.BytesReceived = 0; 1393 child.TotalBytesToReceive = 0; 1394 child.ProgressPercentage = 0; 1395 child.Error = null; 1396 child.ErrorMsg = null; 1397 child.Cancelled = false; 1398 1399 child.Retrying?.Invoke(child); 1400 child.RaisePropertyChanged(null); 1401 } 1402 1403 Retrying?.Invoke(this); 1404 RaisePropertyChanged(null); 1405 DownloadEngine.Add(this); 1406 } 1407 1408 public override string ToString() 1409 { 1410 var sb = new StringBuilder(nameof(DownloadTaskGroup)); 1411 1412 if (!String.IsNullOrEmpty(Name)) 1413 { 1414 sb.Append(", name: " + Name); 1415 } 1416 sb.Append($", downloadStatus: {DownloadStatus}。"); 1417 1418 return sb.ToString(); 1419 } 1420 } 1421 1422 /// <summary> 1423 /// 下载进度监测器 1424 /// </summary> 1425 public sealed class DownloadProgressWatcher : BindableBase, IDisposable 1426 { 1427 private SpeedCalculator _speedCalculator; 1428 private readonly List<DownloadTask> _downloadTasks = new List<DownloadTask>(); 1429 1430 public DownloadProgressWatcher() 1431 { 1432 DownloadProgressChanged += OnDownloadProgressChangedForDownloadSpeed; 1433 DownloadFileCompleted += OnDownloadFileCompletedForDownloadSpeed; 1434 } 1435 1436 public void AppendDownloadTask(DownloadTask downloadTask) 1437 { 1438 if (_downloadTasks.Contains(downloadTask)) 1439 { 1440 return; 1441 } 1442 1443 _downloadTasks.Add(downloadTask); 1444 1445 if (downloadTask.TotalBytesToReceive == 0 && downloadTask != CurrentDownloadTask) 1446 { 1447 var client = downloadTask.CreateDownloadClient?.Invoke() ?? new HttpDownloadClient(downloadTask); 1448 var ret = client.GetFileSize(); 1449 1450 if (!ret.IsSucc) 1451 { 1452 _downloadTasks.Remove(downloadTask); 1453 return; 1454 } 1455 1456 downloadTask.TotalBytesToReceive = ret.Value; 1457 } 1458 1459 DownloadProgressChanged?.Invoke(this); 1460 1461 downloadTask.DownloadProgressChanged -= OnDownloadTaskDownloadProgressChanged; 1462 downloadTask.DownloadProgressChanged += OnDownloadTaskDownloadProgressChanged; 1463 downloadTask.DownloadFileCompleted -= OnDownloadTaskDownloadFileCompleted; 1464 downloadTask.DownloadFileCompleted += OnDownloadTaskDownloadFileCompleted; 1465 downloadTask.Removed -= OnDownloadTaskRemoved; 1466 downloadTask.Removed += OnDownloadTaskRemoved; 1467 } 1468 1469 public void AppendDownloadTaskGroup(DownloadTaskGroup group) 1470 { 1471 foreach (var downloadTask in group.Children) 1472 { 1473 AppendDownloadTask(downloadTask); 1474 } 1475 1476 group.Appended -= AppendDownloadTask; 1477 group.Appended += AppendDownloadTask; 1478 group.DownloadFileCompleted -= OnDownloadTaskGroupDownloadFileCompleted; 1479 group.DownloadFileCompleted += OnDownloadTaskGroupDownloadFileCompleted; 1480 } 1481 1482 1483 public DownloadTask[] DownloadTasks => _downloadTasks.ToArray(); 1484 /// <summary> 1485 /// 下载进度改变或下载完成 1486 /// </summary> 1487 public Action<DownloadProgressWatcher> DownloadProgressChanged { get; set; } 1488 1489 /// <summary> 1490 /// 进度百分比 1491 /// </summary> 1492 public int ProgressPercentage 1493 { 1494 get 1495 { 1496 var fixedSum = _downloadTasks.Sum(q => q.FixedPercentage) / 100 / _downloadTasks.Count; 1497 var totalBytes = (long)(_downloadTasks.Sum(q => q.TotalBytesToReceive) / (1 - fixedSum)); 1498 1499 return totalBytes == 0 ? 0 : (int)(BytesReceived * 100 / totalBytes); 1500 } 1501 } 1502 /// <summary> 1503 /// 总字节数 1504 /// </summary> 1505 public long TotalBytesToReceive 1506 { 1507 get 1508 { 1509 var fixedSum = _downloadTasks.Sum(q => q.FixedPercentage) / 100 / _downloadTasks.Count; 1510 return (long)(_downloadTasks.Sum(q => q.TotalBytesToReceive) / (1 - fixedSum)); 1511 } 1512 } 1513 /// <summary> 1514 /// 已接收字节数 1515 /// </summary> 1516 public long BytesReceived 1517 { 1518 get 1519 { 1520 var totalBytes = TotalBytesToReceive; 1521 var received = 0d; 1522 1523 foreach (var child in _downloadTasks) 1524 { 1525 if (child.FixedPercentage == 0) 1526 { 1527 received += child.BytesReceived; 1528 } 1529 else 1530 { 1531 switch (child.DownloadStatus) 1532 { 1533 case DownloadStatus.Completed: 1534 received += totalBytes * child.FixedPercentage / 100 / _downloadTasks.Count; 1535 break; 1536 case DownloadStatus.Downloading: 1537 received += totalBytes * child.FixedPercentage / 100 / _downloadTasks.Count * child.ProgressPercentage / 100; 1538 break; 1539 } 1540 } 1541 } 1542 1543 return (long)received; 1544 } 1545 } 1546 /// <summary> 1547 /// 下载速度 B/s 1548 /// </summary> 1549 public long? DownloadSpeed { get; private set; } 1550 1551 /// <summary> 1552 /// 下载完成或下载失败或下载取消 1553 /// </summary> 1554 public Action<DownloadProgressWatcher> DownloadFileCompleted { get; set; } 1555 1556 /// <summary> 1557 /// 下载状态 1558 /// </summary> 1559 public DownloadStatus DownloadStatus 1560 { 1561 get 1562 { 1563 var tasks = _downloadTasks.Where(q => q.DownloadStatus != DownloadStatus.Cancelled && q.DownloadStatus != DownloadStatus.Error).ToArray(); 1564 1565 if (tasks.Any(q => q.DownloadStatus == DownloadStatus.Downloading)) 1566 return DownloadStatus.Downloading; 1567 if (tasks.All(q => q.DownloadStatus == DownloadStatus.Waiting)) 1568 return DownloadStatus.Waiting; 1569 else 1570 return DownloadStatus.Completed; 1571 } 1572 } 1573 1574 public DownloadTask CurrentDownloadTask 1575 { 1576 get 1577 { 1578 while (_downloadTasks.Count > 0) 1579 { 1580 var downloadTask = GetDownloadTask(); 1581 if (downloadTask != null) 1582 { 1583 return downloadTask; 1584 } 1585 Thread.Yield(); 1586 } 1587 return null; 1588 } 1589 } 1590 private DownloadTask GetDownloadTask() 1591 { 1592 var status = DownloadStatus; 1593 1594 if (status == DownloadStatus.Waiting) 1595 return _downloadTasks.FirstOrDefault(q => q.DownloadStatus == status); 1596 else if (status == DownloadStatus.Downloading) 1597 return _downloadTasks.FirstOrDefault(q => q.DownloadStatus == status) ?? _downloadTasks.FirstOrDefault(q => q.DownloadStatus == DownloadStatus.Waiting); 1598 else 1599 return _downloadTasks.LastOrDefault(q => q.DownloadStatus == status) ?? _downloadTasks.LastOrDefault(); 1600 } 1601 1602 private void OnDownloadTaskDownloadProgressChanged(DownloadTask downloadTask) 1603 { 1604 DownloadProgressChanged?.Invoke(this); 1605 } 1606 1607 private void OnDownloadTaskDownloadFileCompleted(DownloadTask downloadTask) 1608 { 1609 downloadTask.DownloadFileCompleted -= OnDownloadTaskDownloadFileCompleted; 1610 1611 if (_downloadTasks.All(q => q.DownloadStatus == DownloadStatus.Completed || q.DownloadStatus == DownloadStatus.Cancelled || q.DownloadStatus == DownloadStatus.Error)) 1612 { 1613 DownloadFileCompleted?.Invoke(this); 1614 } 1615 } 1616 1617 private void OnDownloadTaskRemoved(DownloadTask downloadTask) 1618 { 1619 downloadTask.Removed -= OnDownloadTaskRemoved; 1620 _downloadTasks.Remove(downloadTask); 1621 1622 DownloadProgressChanged?.Invoke(this); 1623 } 1624 1625 private void OnDownloadTaskGroupDownloadFileCompleted(DownloadTaskGroup group) 1626 { 1627 group.DownloadFileCompleted -= OnDownloadTaskGroupDownloadFileCompleted; 1628 group.Appended -= AppendDownloadTask; 1629 } 1630 1631 private void OnDownloadProgressChangedForDownloadSpeed(DownloadProgressWatcher watcher) 1632 { 1633 if (DownloadStatus == DownloadStatus.Downloading && _speedCalculator == null && BytesReceived > 0) 1634 { 1635 _speedCalculator = new SpeedCalculator(() => BytesReceived, n => 1636 { 1637 if (DownloadStatus != DownloadStatus.Downloading) 1638 { 1639 if (_speedCalculator != null) 1640 { 1641 _speedCalculator.Dispose(); 1642 _speedCalculator = null; 1643 } 1644 } 1645 DownloadSpeed = n; 1646 DownloadProgressChanged?.Invoke(this); 1647 }); 1648 _speedCalculator.Start(BytesReceived); 1649 } 1650 1651 RaisePropertyChanged(null); 1652 } 1653 1654 private void OnDownloadFileCompletedForDownloadSpeed(DownloadProgressWatcher watcher) 1655 { 1656 if (_speedCalculator != null) 1657 { 1658 _speedCalculator.Dispose(); 1659 _speedCalculator = null; 1660 } 1661 1662 RaisePropertyChanged(null); 1663 } 1664 1665 public void Dispose() 1666 { 1667 if (_speedCalculator != null) 1668 { 1669 _speedCalculator.Dispose(); 1670 _speedCalculator = null; 1671 } 1672 1673 foreach (var downloadTask in _downloadTasks) 1674 { 1675 downloadTask.DownloadProgressChanged -= OnDownloadTaskDownloadProgressChanged; 1676 downloadTask.DownloadFileCompleted -= OnDownloadTaskDownloadFileCompleted; 1677 downloadTask.Removed -= OnDownloadTaskRemoved; 1678 1679 var group = downloadTask.Group; 1680 1681 if (group != null) 1682 { 1683 group.Appended -= AppendDownloadTask; 1684 group.DownloadFileCompleted -= OnDownloadTaskGroupDownloadFileCompleted; 1685 } 1686 } 1687 } 1688 } 1689 1690 1691 1692 public abstract class DownloadClientBase : IDisposable 1693 { 1694 public DownloadClientBase(DownloadTask downloadTask) 1695 { 1696 _downloadTask = downloadTask; 1697 } 1698 1699 public abstract ResultValue<long> GetFileSize(); 1700 1701 public abstract void BeginDownload(); 1702 1703 public abstract void BeginCancel(); 1704 1705 public abstract void Dispose(); 1706 1707 #region Property 1708 1709 public DownloadTask DownloadTask => _downloadTask; 1710 1711 public Action<(DownloadClientBase sender, long BytesReceived, long TotalBytesToReceive, int ProgressPercentage)> DownloadProgressChanged { get; set; } 1712 public Action<(DownloadClientBase sender, Exception Error, bool Cancelled)> DownloadFileCompleted { get; set; } 1713 1714 #endregion 1715 1716 #region Field 1717 1718 private DownloadTask _downloadTask; 1719 1720 #endregion 1721 } 1722 1723 public class HttpDownloadClient : DownloadClientBase 1724 { 1725 public HttpDownloadClient(DownloadTask downloadTask) : base(downloadTask) 1726 { 1727 _webClient = new WebClientPro 1728 { 1729 UseDefaultCredentials = true, 1730 Encoding = Encoding.UTF8, 1731 Timeout = 60_000 * 60, 1732 KeepAlive = true 1733 }; 1734 1735 if (downloadTask.Headers != null) 1736 { 1737 foreach (var kv in downloadTask.Headers) 1738 { 1739 _webClient.Headers.Add(kv.Key, kv.Value); 1740 } 1741 } 1742 1743 _webClient.DownloadProgressChanged += OnDownloadProgressChanged; 1744 _webClient.DownloadFileCompleted += OnDownloadFileCompleted; 1745 } 1746 1747 public override ResultValue<long> GetFileSize() 1748 { 1749 var result = new ResultValue<long>(); 1750 1751 HttpWebRequest req = null; 1752 HttpWebResponse rsp = null; 1753 1754 try 1755 { 1756 req = (HttpWebRequest)HttpWebRequest.Create(DownloadTask.Url); 1757 req.Timeout = 10_000; 1758 req.Method = "HEAD"; 1759 1760 if (DownloadTask.Headers != null) 1761 { 1762 foreach (var kv in DownloadTask.Headers) 1763 { 1764 if (kv.Key == "User-Agent") 1765 { 1766 req.UserAgent = kv.Value; 1767 continue; 1768 } 1769 1770 req.Headers.Add(kv.Key, kv.Value); 1771 } 1772 } 1773 1774 rsp = (HttpWebResponse)req.GetResponse(); 1775 if (rsp.StatusCode != HttpStatusCode.OK || rsp.ContentLength == 0) 1776 { 1777 throw new WebException($"访问下载资源失败,Url:{DownloadTask.Url}"); 1778 } 1779 1780 result.Value = rsp.ContentLength; 1781 result.IsSucc = true; 1782 } 1783 catch (Exception ex) 1784 { 1785 result.Error = ex; 1786 result.Msg = ErrorTexts.HttpError(ex, $"访问下载资源失败,Url:{DownloadTask.Url}", out result.errorTextType); 1787 } 1788 finally 1789 { 1790 rsp?.Close(); 1791 req?.Abort(); 1792 1793 if (!result.IsSucc) 1794 { 1795 if (result.Error != null) 1796 Log.Error(result.Error, result.Msg); 1797 else 1798 Log.Info(result.Msg); 1799 } 1800 } 1801 1802 return result; 1803 } 1804 1805 public override void BeginDownload() 1806 { 1807 FileHelper.DeleteFile(DownloadTask.FilePath); 1808 FileHelper.CreateDirectory(Path.GetDirectoryName(DownloadTask.FilePath)); 1809 1810 var uri = new Uri(DownloadTask.Url, UriKind.RelativeOrAbsolute); 1811 _webClient.DownloadFileAsync(uri, DownloadTask.FilePath); 1812 } 1813 1814 public override void BeginCancel() 1815 { 1816 _webClient.CancelAsync(); 1817 1818 if (_cts != null) 1819 { 1820 _cts.TryCancel(); 1821 _cts = null; 1822 1823 DownloadFileCompleted?.Invoke((this, null, true)); 1824 } 1825 } 1826 1827 public override void Dispose() 1828 { 1829 _webClient.Dispose(); 1830 } 1831 1832 private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 1833 { 1834 DownloadProgressChanged?.Invoke((this, DownloadTask.BytesReceived = e.BytesReceived, DownloadTask.TotalBytesToReceive = e.TotalBytesToReceive, e.ProgressPercentage)); 1835 } 1836 1837 private void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e) 1838 { 1839 if (e.Error != null) 1840 { 1841 var web_ex = e.Error as WebException; 1842 if (web_ex != null && web_ex.Response is HttpWebResponse http_res && http_res.StatusCode == HttpStatusCode.InternalServerError) 1843 { 1844 _cts.TryCancel(); 1845 _cts = new CancellationTokenSource(); 1846 var token = _cts.Token; 1847 token.Register(() => SharedTimer.Unsubscribe(new Action<CancellationToken>(ReDownload))); 1848 1849 try 1850 { 1851 var stream = web_ex.Response.GetResponseStream(); 1852 using (var sr = new StreamReader(stream, Encoding.UTF8)) 1853 { 1854 var res_content = sr.ReadToEnd(); 1855 if (Json.TryParse(res_content, out SaasResult res) && res.code == 201) 1856 { 1857 Log.Info(res.msg); 1858 SharedTimer.Subscribe(30, 30, new Action<CancellationToken>(ReDownload), token); 1859 return; 1860 } 1861 } 1862 } 1863 catch (Exception ex) 1864 { 1865 Log.Error(ex, ErrorTexts.HttpError(ex, "下载")); 1866 } 1867 } 1868 } 1869 DownloadFileCompleted?.Invoke((this, e.Error, e.Cancelled)); 1870 } 1871 1872 private void ReDownload(CancellationToken token) 1873 { 1874 SharedTimer.Unsubscribe(new Action<CancellationToken>(ReDownload)); 1875 1876 if (!token.IsCancellationRequested) 1877 { 1878 BeginDownload(); 1879 } 1880 } 1881 1882 #region Field 1883 1884 private WebClient _webClient; 1885 private CancellationTokenSource _cts; 1886 1887 #endregion 1888 } 1889 1890 public class FtpDownloadClient : DownloadClientBase 1891 { 1892 public FtpDownloadClient(DownloadTask downloadTask) : base(downloadTask) 1893 { 1894 _ftp = (FtpWebRequest)FtpWebRequest.Create(new Uri(downloadTask.Url, UriKind.RelativeOrAbsolute)); 1895 _ftp.Credentials = new NetworkCredential(downloadTask.UserName, downloadTask.Password); 1896 _ftp.Method = WebRequestMethods.Ftp.DownloadFile; 1897 _ftp.UseBinary = true; 1898 } 1899 1900 public override ResultValue<long> GetFileSize() 1901 { 1902 var result = new ResultValue<long>(); 1903 var ftp = (FtpWebRequest)FtpWebRequest.Create(new Uri(DownloadTask.Url)); 1904 1905 try 1906 { 1907 ftp.Credentials = new NetworkCredential(DownloadTask.UserName, DownloadTask.Password); 1908 ftp.UseBinary = true; 1909 ftp.Method = WebRequestMethods.Ftp.GetFileSize; 1910 var res = ftp.GetResponse(); 1911 1912 if (res.ContentLength == 0) 1913 throw new Exception($"访问下载资源失败,Url:{DownloadTask.Url}"); 1914 1915 result.Value = res.ContentLength; 1916 result.IsSucc = true; 1917 } 1918 catch (Exception ex) 1919 { 1920 result.Error = ex; 1921 result.Msg = ErrorTexts.FtpError(ex, "下载文件", out result.errorTextType); 1922 } 1923 finally 1924 { 1925 ftp.Abort(); 1926 1927 if (!result.IsSucc) 1928 { 1929 if (result.Error != null) 1930 Log.Error(result.Error, result.Msg); 1931 else 1932 Log.Info(result.Msg); 1933 } 1934 } 1935 1936 return result; 1937 } 1938 1939 public override void BeginDownload() 1940 { 1941 _cts.TryCancel(); 1942 _cts = new CancellationTokenSource(); 1943 1944 ThreadPool.QueueUserWorkItem(n => 1945 { 1946 var token = (CancellationToken)n; 1947 1948 try 1949 { 1950 FileHelper.DeleteFile(DownloadTask.FilePath); 1951 FileHelper.CreateDirectory(Path.GetDirectoryName(DownloadTask.FilePath)); 1952 1953 var buffLength = 1024; 1954 var buff = new byte[buffLength]; 1955 1956 using (var stream = new FileStream(DownloadTask.FilePath, FileMode.CreateNew, FileAccess.Write)) 1957 { 1958 using (var reqStream = _ftp.GetRequestStream()) 1959 { 1960 var contentLen = 0; 1961 DownloadTask.TotalBytesToReceive = reqStream.Length; 1962 DownloadTask.BytesReceived = 0L; 1963 1964 do 1965 { 1966 contentLen = reqStream.Read(buff, 0, buffLength); 1967 stream.Write(buff, 0, contentLen); 1968 1969 DownloadTask.BytesReceived += contentLen; 1970 1971 DownloadProgressChanged?.Invoke((this, DownloadTask.BytesReceived, reqStream.Length, (int)(DownloadTask.BytesReceived * 100d / reqStream.Length))); 1972 } 1973 while (contentLen != 0 && !token.IsCancellationRequested); 1974 } 1975 } 1976 1977 DownloadFileCompleted?.Invoke((this, null, token.IsCancellationRequested)); 1978 } 1979 catch (Exception ex) 1980 { 1981 DownloadFileCompleted?.Invoke((this, ex, token.IsCancellationRequested)); 1982 } 1983 }, _cts.Token); 1984 } 1985 1986 public override void BeginCancel() 1987 { 1988 if (_cts != null) 1989 { 1990 _cts.TryCancel(); 1991 _cts = null; 1992 } 1993 } 1994 1995 public override void Dispose() 1996 { 1997 _ftp.Abort(); 1998 1999 if (_cts != null) 2000 { 2001 _cts.TryCancel(); 2002 _cts = null; 2003 } 2004 } 2005 2006 #region Field 2007 2008 private FtpWebRequest _ftp; 2009 private CancellationTokenSource _cts; 2010 2011 #endregion 2012 } 2013 2014 public class SftpDownloadClient : DownloadClientBase 2015 { 2016 public SftpDownloadClient(DownloadTask downloadTask) : base(downloadTask) 2017 { 2018 _sftp = new SftpClient(downloadTask.Host, downloadTask.Port, downloadTask.UserName, downloadTask.Password); 2019 } 2020 2021 public override ResultValue<long> GetFileSize() 2022 { 2023 var result = new ResultValue<long>(); 2024 2025 _sftp = new SftpClient(DownloadTask.Host, DownloadTask.Port, DownloadTask.UserName, DownloadTask.Password); 2026 2027 try 2028 { 2029 var folder = Path.GetDirectoryName(DownloadTask.Url); 2030 var fileName = Path.GetFileName(DownloadTask.Url); 2031 2032 _sftp.Connect(); 2033 var files = _sftp.ListDirectory(folder); 2034 var file = files.FirstOrDefault(q => q.Name == fileName); 2035 2036 if (file == null || file.Length == 0) 2037 throw new Exception($"访问下载资源失败,Url:{DownloadTask.Url}"); 2038 2039 result.Value = DownloadTask.TotalBytesToReceive = file.Length; 2040 result.IsSucc = true; 2041 } 2042 catch (Exception ex) 2043 { 2044 result.Error = ex; 2045 result.Msg = ErrorTexts.FtpError(ex, "下载文件", out result.errorTextType); 2046 } 2047 finally 2048 { 2049 _sftp.Dispose(); 2050 2051 if (!result.IsSucc) 2052 { 2053 if (result.Error != null) 2054 Log.Error(result.Error, result.Msg); 2055 else 2056 Log.Info(result.Msg); 2057 } 2058 } 2059 2060 return result; 2061 } 2062 2063 public override void BeginDownload() 2064 { 2065 _cts?.TryCancel(); 2066 _cts = new CancellationTokenSource(); 2067 2068 ThreadPool.QueueUserWorkItem(n => 2069 { 2070 var token = (CancellationToken)n; 2071 2072 try 2073 { 2074 _sftp.Connect(); 2075 2076 FileHelper.DeleteFile(DownloadTask.FilePath); 2077 FileHelper.CreateDirectory(Path.GetDirectoryName(DownloadTask.FilePath)); 2078 2079 using (var stream = new FileStream(DownloadTask.FilePath, FileMode.CreateNew, FileAccess.Write)) 2080 { 2081 if (DownloadTask.TotalBytesToReceive == 0) 2082 { 2083 var ret = GetFileSize(); 2084 2085 if (!ret.IsSucc) 2086 { 2087 DownloadFileCompleted?.Invoke((this, ret.Error, token.IsCancellationRequested)); 2088 return; 2089 } 2090 2091 DownloadTask.TotalBytesToReceive = ret.Value; 2092 } 2093 2094 _sftp.DownloadFile(DownloadTask.Url, stream, m => { DownloadTask.BytesReceived = (long)m; DownloadProgressChanged?.Invoke((this, DownloadTask.BytesReceived, DownloadTask.TotalBytesToReceive, (int)(m * 100d / DownloadTask.TotalBytesToReceive))); }); 2095 2096 if (DownloadTask.BytesReceived != DownloadTask.TotalBytesToReceive) 2097 { 2098 DownloadTask.BytesReceived = DownloadTask.TotalBytesToReceive; 2099 DownloadProgressChanged?.Invoke((this, DownloadTask.BytesReceived, DownloadTask.TotalBytesToReceive, 100)); 2100 } 2101 } 2102 2103 DownloadFileCompleted?.Invoke((this, null, token.IsCancellationRequested)); 2104 } 2105 catch (Exception ex) 2106 { 2107 DownloadFileCompleted?.Invoke((this, ex, token.IsCancellationRequested)); 2108 } 2109 }, _cts.Token); 2110 } 2111 2112 public override void BeginCancel() 2113 { 2114 if (_cts != null) 2115 { 2116 _cts.TryCancel(); 2117 _cts = null; 2118 } 2119 } 2120 2121 public override void Dispose() 2122 { 2123 _sftp.Dispose(); 2124 2125 if (_cts != null) 2126 { 2127 _cts.TryCancel(); 2128 _cts = null; 2129 } 2130 } 2131 2132 #region Field 2133 2134 private SftpClient _sftp; 2135 private CancellationTokenSource _cts; 2136 2137 #endregion 2138 } 2139 2140 public class CustomDownloadClient : DownloadClientBase 2141 { 2142 public CustomDownloadClient(DownloadTask downloadTask) : base(downloadTask) { } 2143 2144 public override ResultValue<long> GetFileSize() => GetFileSizeCalled == null ? ResultValue<long>.Succeed(0) : GetFileSizeCalled.Invoke(); 2145 2146 public override void BeginDownload() 2147 { 2148 if (BeginDownloadCalled == null) 2149 { 2150 DownloadTask.DownloadStatus = DownloadStatus.Completed; 2151 DownloadFileCompleted?.Invoke((this, null, false)); 2152 } 2153 else 2154 { 2155 _cts?.TryCancel(); 2156 _cts = new CancellationTokenSource(); 2157 BeginDownloadCalled.Invoke(this, _cts.Token); 2158 } 2159 } 2160 2161 public override void BeginCancel() 2162 { 2163 if (_cts != null) 2164 { 2165 _cts.TryCancel(); 2166 _cts = null; 2167 } 2168 2169 if (BeginCancelCalled != null) 2170 { 2171 BeginCancelCalled.Invoke(this); 2172 } 2173 else 2174 { 2175 DownloadFileCompleted?.Invoke((this, null, true)); 2176 } 2177 } 2178 2179 public override void Dispose() => DisposeCalled?.Invoke(this); 2180 2181 #region Property 2182 2183 public Func<ResultValue<long>> GetFileSizeCalled { get; set; } 2184 2185 public Action<CustomDownloadClient, CancellationToken> BeginDownloadCalled { get; set; } 2186 2187 public Action<CustomDownloadClient> BeginCancelCalled { get; set; } 2188 2189 public Action<CustomDownloadClient> DisposeCalled { get; set; } 2190 2191 #endregion 2192 2193 #region Field 2194 2195 private CancellationTokenSource _cts; 2196 2197 #endregion 2198 } 2199 2200 /// <summary> 2201 /// 星原断点下载 2202 /// </summary> 2203 public class HttpRangeDownloadClient : DownloadClientBase 2204 { 2205 public HttpRangeDownloadClient(DownloadTask downloadTask) : base(downloadTask) 2206 { 2207 _webClient = new WebClientPro 2208 { 2209 UseDefaultCredentials = true, 2210 Encoding = Encoding.UTF8, 2211 Timeout = 60_000 * 60, 2212 KeepAlive = true 2213 }; 2214 2215 if (downloadTask.Headers != null) 2216 { 2217 foreach (var kv in downloadTask.Headers) 2218 { 2219 _webClient.Headers.Add(kv.Key, kv.Value); 2220 } 2221 } 2222 2223 _webClient.DownloadProgressChanged += OnDownloadProgressChanged; 2224 _webClient.DownloadFileCompleted += OnDownloadFileCompleted; 2225 } 2226 2227 public override ResultValue<long> GetFileSize() 2228 { 2229 var result = new ResultValue<long>(); 2230 2231 HttpWebRequest req = null; 2232 HttpWebResponse rsp = null; 2233 2234 try 2235 { 2236 req = (HttpWebRequest)HttpWebRequest.Create(DownloadTask.Url); 2237 req.Timeout = 10_000; 2238 req.Method = "HEAD"; 2239 2240 if (DownloadTask.Headers != null) 2241 { 2242 foreach (var kv in DownloadTask.Headers) 2243 { 2244 if (kv.Key == "User-Agent") 2245 { 2246 req.UserAgent = kv.Value; 2247 continue; 2248 } 2249 2250 req.Headers.Add(kv.Key, kv.Value); 2251 } 2252 } 2253 2254 rsp = (HttpWebResponse)req.GetResponse(); 2255 if (rsp.StatusCode != HttpStatusCode.OK || rsp.ContentLength == 0) 2256 { 2257 throw new WebException($"访问下载资源失败,Url:{DownloadTask.Url}"); 2258 } 2259 result.Value = rsp.ContentLength; 2260 2261 var range = rsp.Headers.Get("Content-Range"); 2262 if (range != null) 2263 { 2264 var total = range.Split('/').LastOrDefault().TryConvert<long>(out var succee); 2265 if (succee) 2266 { 2267 result.Value = total; 2268 } 2269 } 2270 result.IsSucc = true; 2271 } 2272 catch (Exception ex) 2273 { 2274 result.Msg = ErrorTexts.HttpError(ex, $"访问下载资源失败,Url:{DownloadTask.Url}", out result.errorTextType); 2275 result.Error = ex; 2276 } 2277 finally 2278 { 2279 rsp?.Close(); 2280 req?.Abort(); 2281 2282 if (!result.IsSucc) 2283 { 2284 if (result.Error != null) 2285 Log.Error(result.Error, result.Msg); 2286 else 2287 Log.Info(result.Msg); 2288 } 2289 } 2290 2291 return result; 2292 } 2293 2294 public override void BeginDownload() 2295 { 2296 FileHelper.DeleteFile(DownloadTask.FilePath); 2297 FileHelper.CreateDirectory(Path.GetDirectoryName(DownloadTask.FilePath)); 2298 2299 if (DownloadTask.TotalBytesToReceive == 0) 2300 { 2301 var ret = GetFileSize(); 2302 if (!ret.IsSucc || ret.Value == 0) 2303 { 2304 DownloadFileCompleted?.Invoke((this, ret.Error ?? new Exception($"访问下载资源失败,Url:{DownloadTask.Url}"), false)); 2305 return; 2306 } 2307 DownloadTask.TotalBytesToReceive = ret.Value; 2308 } 2309 2310 DownloadTask.DownloadStatus = DownloadStatus.Downloading; 2311 2312 var downloadingFile = new FileInfo(DownloadTask.FilePath + ".downloading"); 2313 var downloadedSize = downloadingFile.Exists ? downloadingFile.Length : 0L; 2314 var chunkSize = Math.Min(ChunkSize, DownloadTask.TotalBytesToReceive - downloadedSize); 2315 _webClient.From = downloadedSize; 2316 _webClient.To = downloadedSize + chunkSize; 2317 var chunkFile = new FileInfo(DownloadTask.FilePath + ".chunk"); 2318 if (chunkFile.Exists) 2319 { 2320 FileHelper.DeleteFile(chunkFile.FullName); 2321 } 2322 2323 var uri = new Uri(DownloadTask.Url + $"&fileName={chunkFile.Name}", UriKind.RelativeOrAbsolute); 2324 Log.Info(uri.OriginalString); 2325 _webClient.DownloadFileAsync(uri, chunkFile.FullName); 2326 } 2327 2328 public override void BeginCancel() 2329 { 2330 _webClient.CancelAsync(); 2331 2332 if (_cts != null) 2333 { 2334 _cts.TryCancel(); 2335 _cts = null; 2336 2337 DownloadFileCompleted?.Invoke((this, null, true)); 2338 } 2339 } 2340 2341 public override void Dispose() 2342 { 2343 _webClient.Dispose(); 2344 } 2345 2346 private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 2347 { 2348 if (DownloadTask.TotalBytesToReceive < e.TotalBytesToReceive) 2349 { 2350 DownloadTask.TotalBytesToReceive = e.TotalBytesToReceive; 2351 } 2352 DownloadTask.BytesReceived = Math.Min(_webClient.From + e.BytesReceived, DownloadTask.TotalBytesToReceive); 2353 DownloadTask.ProgressPercentage = (int)(DownloadTask.BytesReceived * 100.0 / DownloadTask.TotalBytesToReceive); 2354 DownloadProgressChanged?.Invoke((this, DownloadTask.BytesReceived, DownloadTask.TotalBytesToReceive, DownloadTask.ProgressPercentage)); 2355 } 2356 2357 private void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e) 2358 { 2359 if (e.Error != null) 2360 { 2361 var web_ex = e.Error as WebException; 2362 if (web_ex != null && web_ex.Response is HttpWebResponse http_res && http_res.StatusCode == HttpStatusCode.InternalServerError) 2363 { 2364 _cts.TryCancel(); 2365 _cts = new CancellationTokenSource(); 2366 var token = _cts.Token; 2367 token.Register(() => SharedTimer.Unsubscribe(new Action<CancellationToken>(ReDownload))); 2368 2369 try 2370 { 2371 var stream = web_ex.Response.GetResponseStream(); 2372 using (var sr = new StreamReader(stream, Encoding.UTF8)) 2373 { 2374 var res_content = sr.ReadToEnd(); 2375 if (Json.TryParse(res_content, out SaasResult res) && res.code == 201) 2376 { 2377 Log.Info(res.msg); 2378 SharedTimer.Subscribe(30, 30, new Action<CancellationToken>(ReDownload), token); 2379 return; 2380 } 2381 } 2382 } 2383 catch (Exception ex) 2384 { 2385 Log.Error(ex, ErrorTexts.HttpError(ex, "下载")); 2386 } 2387 } 2388 } 2389 else if (!e.Cancelled) 2390 { 2391 // 下载成功,合并分包 2392 var chunkFile = new FileInfo(DownloadTask.FilePath + ".chunk"); 2393 var downloadingFile = new FileInfo(DownloadTask.FilePath + ".downloading"); 2394 var margeFlag = false; 2395 if (!downloadingFile.Exists) 2396 { 2397 margeFlag = FileHelper.MoveFile(chunkFile.FullName, downloadingFile.FullName); 2398 } 2399 else 2400 { 2401 margeFlag = FileHelper.MargeFiles(downloadingFile.FullName, chunkFile.FullName); 2402 } 2403 if (!margeFlag) 2404 { 2405 DownloadFileCompleted?.Invoke((this, new Exception("分包下载,合并下载文件失败!"), false)); 2406 return; 2407 } 2408 if (DownloadTask.BytesReceived == DownloadTask.TotalBytesToReceive) 2409 { 2410 FileHelper.MoveFile(downloadingFile.FullName, DownloadTask.FilePath); 2411 } 2412 else 2413 { 2414 // 继续下载剩余分包 2415 BeginDownload(); 2416 return; 2417 } 2418 } 2419 DownloadFileCompleted?.Invoke((this, e.Error, e.Cancelled)); 2420 } 2421 2422 private void ReDownload(CancellationToken token) 2423 { 2424 SharedTimer.Unsubscribe(new Action<CancellationToken>(ReDownload)); 2425 2426 if (!token.IsCancellationRequested) 2427 { 2428 BeginDownload(); 2429 } 2430 } 2431 2432 #region Property 2433 2434 /// <summary> 2435 /// 分包大小,默认30M 2436 /// </summary> 2437 public long ChunkSize { get; set; } = 1024 * 1024 * 30; 2438 2439 #endregion 2440 2441 #region Field 2442 2443 private WebClientPro _webClient; 2444 private CancellationTokenSource _cts; 2445 2446 #endregion 2447 } 2448 }
上传器关键源码:

1 using Gnt.DTOs; 2 using Gnt.Events; 3 using Gnt.Events.PubSubEvents; 4 using Gnt.Extensions; 5 using Gnt.Ioc; 6 using Gnt.Mvvm; 7 using Renci.SshNet; 8 using System; 9 using System.Collections.Generic; 10 using System.IO; 11 using System.Linq; 12 using System.Net; 13 using System.Text; 14 using System.Threading; 15 using System.Threading.Tasks; 16 17 namespace Gnt.Utils 18 { 19 /// <summary> 20 /// 上传器 21 /// </summary> 22 public sealed class UploadEngine : IDisposable 23 { 24 #region Static 25 26 /// <summary> 27 /// 已创建上传器 28 /// </summary> 29 private static Dictionary<string, UploadEngine> _uploadEngines = new Dictionary<string, UploadEngine>(); 30 31 /// <summary> 32 /// 创建上传器 33 /// </summary> 34 /// <param name="name">上传器名称</param> 35 /// <param name="maximumCount">最大同时上传数</param> 36 /// <param name="createdNew">是否创建了新的</param> 37 /// <returns></returns> 38 public static UploadEngine Create(string name, int maximumCount, out bool createdNew) 39 { 40 lock (_uploadEngines) 41 { 42 createdNew = false; 43 44 if (_uploadEngines.TryGetValue(name, out UploadEngine uploadEngine)) 45 return uploadEngine; 46 47 uploadEngine = new UploadEngine(name, maximumCount); 48 _uploadEngines.Add(name, uploadEngine); 49 50 createdNew = true; 51 return uploadEngine; 52 } 53 } 54 55 /// <summary> 56 /// 获取上传器 57 /// </summary> 58 /// <param name="name"></param> 59 /// <returns></returns> 60 public static UploadEngine Get(string name) 61 { 62 lock (_uploadEngines) 63 { 64 if (_uploadEngines.TryGetValue(name, out UploadEngine uploadEngine)) 65 return uploadEngine; 66 return null; 67 } 68 } 69 70 #endregion 71 72 /// <summary> 73 /// 上传器 74 /// </summary> 75 /// <param name="name">上传器名称</param> 76 /// <param name="maximumCount">最大同时上传数</param> 77 private UploadEngine(string name, int maximumCount) 78 { 79 Name = name; 80 _maximumCount = maximumCount; 81 _semaphore = new Semaphore(maximumCount, maximumCount); 82 } 83 84 #region Method 85 86 /// <summary> 87 /// 释放资源 88 /// </summary> 89 public void Dispose() 90 { 91 lock (locker) 92 { 93 foreach (var item in _waiting.ToArray()) 94 { 95 Remove(item); 96 } 97 foreach (var item in _uploading.Keys.ToArray()) 98 { 99 Remove(item); 100 } 101 } 102 103 lock (_uploadEngines) 104 { 105 if (_semaphore != null) 106 { 107 _semaphore.Dispose(); 108 _semaphore = null; 109 } 110 111 _uploadEngines.Remove(Name); 112 } 113 } 114 115 /// <summary> 116 /// 加入上传任务 117 /// </summary> 118 /// <param name="uploadTask"></param> 119 public void Add(UploadTask uploadTask) 120 { 121 lock (locker) 122 { 123 if (_uploading.ContainsKey(uploadTask) || _waiting.Contains(uploadTask)) 124 return; 125 126 _waiting.Add(uploadTask); 127 uploadTask.UploadEngine = this; 128 129 if (_thread == null) 130 { 131 // 启动轮询线程 132 _thread = new Thread(new ThreadStart(Run)) { IsBackground = true }; 133 _thread.Start(); 134 } 135 } 136 } 137 138 /// <summary> 139 /// 加入上传任务组 140 /// </summary> 141 /// <param name="multipleUploadTask"></param> 142 public void Add(UploadTaskGroup uploadTaskGroup) 143 { 144 lock (locker) 145 { 146 foreach (var uploadTask in uploadTaskGroup.Children) 147 { 148 Add(uploadTask); 149 } 150 uploadTaskGroup.UploadEngine = this; 151 } 152 } 153 154 /// <summary> 155 /// 移除上传任务 156 /// </summary> 157 /// <param name="uploadTask"></param> 158 public void Remove(UploadTask uploadTask) 159 { 160 UploadClientBase uploadClient = null; 161 162 lock (locker) 163 { 164 if (_uploading.TryGetValue(uploadTask, out uploadClient)) 165 _uploading.Remove(uploadTask); 166 else if (_waiting.Remove(uploadTask)) 167 { 168 uploadTask.UploadStatus = UploadStatus.Cancelled; 169 uploadTask.UploadDataCompleted?.Invoke(uploadTask); 170 171 if (uploadTask.Group != null) 172 uploadTask.Group.UploadDataCompleted?.Invoke(uploadTask.Group); 173 } 174 } 175 176 // 取消上传 177 uploadClient?.BeginCancel(); 178 } 179 180 /// <summary> 181 /// 移除指定上传任务 182 /// </summary> 183 /// <param name="predicate"></param> 184 public void Remove(Func<UploadTask, bool> predicate) 185 { 186 lock (locker) 187 { 188 foreach (var item in _waiting.Where(predicate).ToArray()) 189 { 190 Remove(item); 191 } 192 193 foreach (var item in _uploading.Keys.Where(predicate).ToArray()) 194 { 195 Remove(item); 196 } 197 } 198 } 199 200 /// <summary> 201 /// 移除指定上传任务组 202 /// </summary> 203 /// <param name="predicate"></param> 204 public void Remove(Func<UploadTaskGroup, bool> predicate) 205 { 206 lock (locker) 207 { 208 foreach (var item in _waiting.Where(q => q.Group != null && predicate.Invoke(q.Group)).ToArray()) 209 { 210 Remove(item); 211 } 212 213 foreach (var item in _uploading.Keys.Where(q => q.Group != null && predicate.Invoke(q.Group)).ToArray()) 214 { 215 Remove(item); 216 } 217 } 218 } 219 220 /// <summary> 221 /// 移除上传任务组 222 /// </summary> 223 /// <param name="uploadTaskGroup"></param> 224 public void Remove(UploadTaskGroup uploadTaskGroup) 225 { 226 lock (locker) 227 { 228 foreach (var uploadTask in uploadTaskGroup.Children) 229 { 230 Remove(uploadTask); 231 } 232 } 233 } 234 235 /// <summary> 236 /// 移除等待中的上传任务,并且不触发事件 237 /// </summary> 238 /// <param name="uploadTask"></param> 239 public bool RemoveWaitingTask(UploadTask uploadTask) 240 { 241 lock (locker) 242 { 243 if (_waiting.Remove(uploadTask)) 244 { 245 uploadTask.UploadStatus = UploadStatus.Cancelled; 246 247 if (uploadTask.Group != null) 248 { 249 var group = uploadTask.Group; 250 251 uploadTask.Group = null; 252 group.Children.Remove(uploadTask); 253 } 254 255 uploadTask.Removed?.Invoke(uploadTask); 256 257 return true; 258 } 259 } 260 261 return false; 262 } 263 264 /// <summary> 265 /// 插入上传任务 266 /// </summary> 267 /// <param name="uploadTask"></param> 268 public void Unshift(UploadTask uploadTask) 269 { 270 lock (locker) 271 { 272 _waiting.Insert(0, uploadTask); 273 uploadTask.UploadEngine = this; 274 275 if (uploadTask.Group != null) 276 { 277 uploadTask.Group.Children.Add(uploadTask); 278 uploadTask.Group.Appended?.Invoke(uploadTask); 279 } 280 } 281 } 282 283 /// <summary> 284 /// 插入上传任务 285 /// </summary> 286 public void UnshiftBefore(UploadTask newUploadTask, UploadTask beforeUploadTask) 287 { 288 lock (locker) 289 { 290 var index = _waiting.IndexOf(beforeUploadTask); 291 _waiting.Insert(index, newUploadTask); 292 newUploadTask.UploadEngine = this; 293 294 if (newUploadTask.Group != null) 295 { 296 index = newUploadTask.Group.Children.IndexOf(beforeUploadTask); 297 if (index == -1) 298 { 299 newUploadTask.Group.Children.Add(newUploadTask); 300 } 301 else 302 { 303 newUploadTask.Group.Children.Insert(index, newUploadTask); 304 } 305 newUploadTask.Group.Appended?.Invoke(newUploadTask); 306 } 307 } 308 } 309 310 /// <summary> 311 /// 获取上传任务 312 /// </summary> 313 /// <param name="status">上传状态 0:全部|1:等待上传|2:上传中</param> 314 /// <returns></returns> 315 public UploadTask[] GetUploadTasks(int status = 0) 316 { 317 lock (locker) 318 { 319 switch (status) 320 { 321 case 1: 322 return _waiting.ToArray(); 323 case 2: 324 return _uploading.Keys.ToArray(); 325 default: 326 var list = new List<UploadTask>(_waiting.Count + _uploading.Count); 327 list.AddRange(_waiting); 328 list.AddRange(_uploading.Keys); 329 return list.ToArray(); 330 } 331 } 332 } 333 334 /// <summary> 335 /// 获取上传任务组 336 /// </summary> 337 /// <param name="status">上传状态 0:全部|1:等待上传|2:上传中</param> 338 /// <returns></returns> 339 public UploadTaskGroup[] GetUploadTaskGroups(int status = 0) 340 { 341 lock (locker) 342 { 343 var list = new List<UploadTaskGroup>(); 344 list.AddRange(_waiting.Where(q => q.Group != null).Select(q => q.Group)); 345 list.AddRange(_uploading.Keys.Where(q => q.Group != null).Select(q => q.Group)); 346 var query = list.Distinct(); 347 348 switch (status) 349 { 350 case 1: 351 return query.Where(q => q.UploadStatus == UploadStatus.Waiting).ToArray(); 352 case 2: 353 return query.Where(q => q.UploadStatus == UploadStatus.Uploading).ToArray(); 354 default: 355 return query.ToArray(); 356 } 357 } 358 } 359 360 public bool AnyTask(Func<UploadTask, bool> predicate = null) 361 { 362 return (predicate == null ? _waiting.Any() : _waiting.Any(predicate)) || (predicate == null ? _uploading.Any() : _uploading.Keys.Any(predicate)); 363 } 364 365 public Task<bool> AnyTaskGroupAsync(Func<UploadTaskGroup, bool> predicate) 366 { 367 return Task.Run(() => AnyTaskGroup(predicate)); 368 } 369 370 public bool AnyTaskGroup(Func<UploadTaskGroup, bool> predicate) 371 { 372 return _waiting.Where(q => q.Group != null).Select(q => q.Group).Any(predicate) || _uploading.Keys.Where(q => q.Group != null).Select(q => q.Group).Any(predicate); 373 } 374 375 /// <summary> 376 /// 查询第一个符合描述的上传任务 377 /// </summary> 378 /// <param name="predicate"></param> 379 /// <returns></returns> 380 public UploadTask GetUploadTask(Func<UploadTask, bool> predicate) 381 { 382 lock (locker) 383 { 384 return _waiting.FirstOrDefault(predicate) ?? _uploading.Keys.FirstOrDefault(predicate); 385 } 386 } 387 388 /// <summary> 389 /// 查询第一个符合描述的上传任务组 390 /// </summary> 391 /// <param name="predicate"></param> 392 /// <returns></returns> 393 public UploadTaskGroup GetUploadTaskGroup(Func<UploadTaskGroup, bool> predicate) 394 { 395 lock (locker) 396 { 397 var group = _waiting.Where(q => q.Group != null).Select(q => q.Group).FirstOrDefault(predicate); 398 399 if (group != null) 400 return group; 401 402 return _uploading.Keys.Where(q => q.Group != null).Select(q => q.Group).FirstOrDefault(predicate); 403 } 404 } 405 406 /// <summary> 407 /// 查询第一个符合描述的上传任务 408 /// </summary> 409 /// <param name="predicate"></param> 410 /// <returns></returns> 411 public UploadTask GetOrAdd(Func<UploadTask, bool> predicate, Func<UploadTask> create) 412 { 413 lock (locker) 414 { 415 var task = _waiting.FirstOrDefault(predicate); 416 417 if (task != null) 418 return task; 419 420 task = _uploading.Keys.FirstOrDefault(predicate); 421 422 if (task != null) 423 return task; 424 425 task = create.Invoke(); 426 427 if (task != null) 428 Add(task); 429 430 return task; 431 } 432 } 433 434 /// <summary> 435 /// 查询第一个符合描述的上传任务组 436 /// </summary> 437 /// <param name="predicate"></param> 438 /// <returns></returns> 439 public UploadTaskGroup GetOrAdd(Func<UploadTaskGroup, bool> predicate, Func<UploadTaskGroup> create) 440 { 441 lock (locker) 442 { 443 var group = _waiting.Where(q => q.Group != null).Select(q => q.Group).FirstOrDefault(predicate); 444 445 if (group != null) 446 return group; 447 448 group = _uploading.Keys.Where(q => q.Group != null).Select(q => q.Group).FirstOrDefault(predicate); 449 450 if (group != null) 451 return group; 452 453 group = create.Invoke(); 454 455 if (group != null) 456 Add(group); 457 458 return group; 459 } 460 } 461 462 /// <summary> 463 /// 开始轮询等待上传队列 464 /// </summary> 465 private void Run() 466 { 467 try 468 { 469 ContainerLocator.Current.Resolve<IEventAggregator>().GetEvent<IsIdleEvent>().Publish((this, false)); 470 471 while (_semaphore != null) 472 { 473 var ret = _semaphore.WaitOne(10000); 474 475 lock (locker) 476 { 477 if (!ret) 478 { 479 if (_uploading.Count < _maximumCount) 480 { 481 // 到这里一定是代码出错了 482 Log.Debug($"等待信号失败,强制释放{_maximumCount - _uploading.Count}信号量"); 483 484 try 485 { 486 TryRelease(_maximumCount - _uploading.Count); 487 } 488 catch { } 489 } 490 else if (_waiting.Count == 0 && _uploading.Count == 0) 491 { 492 _thread = null; 493 break; 494 } 495 496 continue; 497 } 498 499 if (_waiting.Count == 0 && _uploading.Count == 0) 500 { 501 _thread = null; 502 TryRelease(); // 耗费了一个信号,要释放一个 503 break; 504 } 505 506 if (_waiting.Count > 0) 507 { 508 var uploadTask = _waiting.First(); 509 _waiting.Remove(uploadTask); 510 _uploading.Add(uploadTask, null); 511 512 ThreadPool.QueueUserWorkItem(Start, uploadTask); 513 } 514 } 515 } 516 } 517 finally 518 { 519 ContainerLocator.Current.Resolve<IEventAggregator>().GetEvent<IsIdleEvent>().Publish((this, true)); 520 } 521 } 522 523 /// <summary> 524 /// 开始新的上传任务 525 /// </summary> 526 /// <param name="state"></param> 527 private void Start(object state) 528 { 529 var uploadTask = (UploadTask)state; 530 UploadClientBase uploadClient = null; 531 532 try 533 { 534 if (uploadTask.UploadStatus == UploadStatus.Completed) 535 { 536 // 跳过,不触发事件 537 lock (locker) 538 { 539 _uploading.Remove(uploadTask); 540 541 if (_uploading.Count < _maximumCount) 542 TryRelease(_maximumCount - _uploading.Count); 543 } 544 return; 545 } 546 547 var groupStatus = uploadTask.Group == null ? UploadStatus.Waiting : uploadTask.Group.UploadStatus; 548 uploadTask.UploadStatus = UploadStatus.Uploading; 549 uploadTask.BeginTime = DateTime.Now; 550 551 Log.Info($"开始上传,URL:{uploadTask.Url}"); 552 553 if (uploadTask.PreviewUpload != null) 554 { 555 var ret = uploadTask.PreviewUpload.Invoke(uploadTask); 556 557 if (!ret.IsSucc) 558 throw ret.Error ?? new Exception(); 559 560 if (uploadTask.UploadStatus == UploadStatus.Completed) 561 { 562 uploadTask.UploadDataCompleted?.Invoke(uploadTask); 563 564 lock (locker) 565 { 566 _uploading.Remove(uploadTask); 567 568 if (_uploading.Count < _maximumCount) 569 TryRelease(_maximumCount - _uploading.Count); 570 } 571 572 return; 573 } 574 } 575 576 // 判断是否上传任务组 577 if (uploadTask.Group != null && uploadTask.Group.PreviewUpload != null) 578 { 579 var ret = uploadTask.Group.PreviewUpload.Invoke(uploadTask.Group); 580 581 if (!ret.IsSucc) 582 throw ret.Error ?? new Exception(); 583 if (uploadTask.Group.UploadStatus == UploadStatus.Completed) 584 { 585 uploadTask.Group.UploadDataCompleted?.Invoke(uploadTask.Group); 586 587 lock (locker) 588 { 589 Remove(uploadTask.Group); 590 591 if (_uploading.Count < _maximumCount) 592 TryRelease(_maximumCount - _uploading.Count); 593 } 594 595 return; 596 } 597 } 598 599 lock (locker) 600 { 601 if (!_uploading.ContainsKey(uploadTask)) 602 { 603 throw new TaskCanceledException(); 604 } 605 606 uploadClient = uploadTask.CreateUploadClient?.Invoke() ?? new HttpUploadClient(uploadTask); 607 uploadClient.UploadProgressChanged -= OnUploadProgressChanged; 608 uploadClient.UploadProgressChanged += OnUploadProgressChanged; 609 uploadClient.UploadDataCompleted -= OnUploadDataCompleted; 610 uploadClient.UploadDataCompleted += OnUploadDataCompleted; 611 612 _uploading[uploadTask] = uploadClient; 613 } 614 615 uploadTask.UploadStatus = UploadStatus.Uploading; 616 uploadClient.BeginUpload(); 617 } 618 catch (Exception ex) 619 { 620 Log.Info($"上传文件失败\tUrl:{uploadTask.Url}\tMsg:{ex.Message}"); 621 622 if (ex is TaskCanceledException) 623 { 624 uploadTask.UploadStatus = UploadStatus.Cancelled; 625 } 626 else 627 { 628 var msg = ErrorTexts.HttpError(ex, "上传文件"); 629 uploadTask.ErrorMsg = msg; 630 Log.Error(uploadTask.Error = ex, msg); 631 uploadTask.UploadStatus = UploadStatus.Error; 632 } 633 634 uploadTask.UploadDataCompleted?.Invoke(uploadTask); 635 636 lock (locker) 637 { 638 if (uploadTask.Group != null) 639 { 640 uploadTask.Group.UploadDataCompleted?.Invoke(uploadTask.Group); 641 Remove(uploadTask.Group); 642 } 643 else 644 _uploading.Remove(uploadTask); 645 646 if (_uploading.Count < _maximumCount) 647 TryRelease(_maximumCount - _uploading.Count); 648 } 649 650 uploadClient?.Dispose(); 651 } 652 } 653 654 /// <summary> 655 /// 上传进度改变或上传完成 656 /// </summary> 657 /// <param name="sender"></param> 658 /// <param name="e"></param> 659 private void OnUploadProgressChanged((UploadClientBase sender, long BytesSent, long TotalBytesToSend, int ProgressPercentage) e) 660 { 661 var uploadClient = e.sender; 662 var uploadTask = uploadClient.UploadTask; 663 664 uploadTask.ProgressPercentage = e.ProgressPercentage; 665 uploadTask.TotalBytesToSend = e.TotalBytesToSend; 666 uploadTask.BytesSent = e.BytesSent; 667 668 if (uploadTask.UploadStatus == UploadStatus.Waiting) 669 uploadTask.UploadStatus = UploadStatus.Uploading; 670 671 uploadTask.UploadProgressChanged?.Invoke(uploadTask); 672 uploadTask.Group?.UploadProgressChanged?.Invoke(uploadTask.Group); 673 } 674 675 /// <summary> 676 /// 上传完成或上传失败或上传取消 677 /// </summary> 678 /// <param name="sender"></param> 679 /// <param name="e"></param> 680 private void OnUploadDataCompleted((UploadClientBase sender, Exception Error, bool Cancelled) e) 681 { 682 var uploadClient = e.sender; 683 var uploadTask = uploadClient.UploadTask; 684 685 uploadClient.UploadProgressChanged -= OnUploadProgressChanged; 686 uploadClient.UploadDataCompleted -= OnUploadDataCompleted; 687 688 var groupStatus = uploadTask.Group?.UploadStatus; 689 690 try 691 { 692 uploadTask.EndTime = DateTime.Now; 693 694 if (e.Cancelled) 695 { 696 uploadTask.UploadStatus = UploadStatus.Cancelled; 697 698 // 取消整组 699 if (uploadTask.Group != null) 700 { 701 Remove(uploadTask.Group); 702 uploadTask.Group.UploadDataCompleted?.Invoke(uploadTask.Group); 703 } 704 } 705 else if (e.Error != null) 706 { 707 var msg = ErrorTexts.HttpError(e.Error, "上传文件"); 708 uploadTask.ErrorMsg = msg; 709 uploadTask.Error = e.Error; 710 uploadTask.UploadStatus = UploadStatus.Error; 711 712 // 取消整组 713 if (uploadTask.Group != null) 714 { 715 Remove(uploadTask.Group); 716 uploadTask.Group.UploadDataCompleted?.Invoke(uploadTask.Group); 717 } 718 } 719 else if (uploadTask.UploadStatus != UploadStatus.Completed) 720 { 721 if (uploadTask.TotalBytesToSend == 0 || uploadTask.TotalBytesToSend != uploadTask.BytesSent) 722 { 723 var msg = uploadTask.TotalBytesToSend == 0 ? ErrorTexts.Get(ErrorTextType.ConnectFailed, "下载") : ErrorTexts.Get(ErrorTextType.NetworkDisconnect); 724 uploadTask.ErrorMsg = msg; 725 uploadTask.Error = new Exception(msg); 726 uploadTask.UploadStatus = UploadStatus.Error; 727 728 // 取消整组 729 if (uploadTask.Group != null) 730 { 731 Remove(uploadTask.Group); 732 uploadTask.Group.UploadDataCompleted?.Invoke(uploadTask.Group); 733 } 734 } 735 else 736 { 737 uploadTask.UploadStatus = UploadStatus.Completed; 738 } 739 } 740 741 uploadTask.UploadDataCompleted?.Invoke(uploadTask); 742 743 if (uploadTask.Group != null && uploadTask.Group.UploadDataCompleted != null && uploadTask.Group.UploadStatus == UploadStatus.Completed) 744 uploadTask.Group.UploadDataCompleted.Invoke(uploadTask.Group); 745 746 uploadTask.CompletedFlag = uploadTask.UploadStatus == UploadStatus.Completed; 747 748 lock (locker) 749 { 750 _uploading.Remove(uploadTask); 751 752 if (_uploading.Count < _maximumCount) 753 TryRelease(_maximumCount - _uploading.Count); 754 } 755 } 756 catch (Exception ex) 757 { 758 try 759 { 760 var msg = ErrorTexts.HttpError(ex, "上传文件"); 761 uploadTask.ErrorMsg = msg; 762 uploadTask.Error = ex; 763 uploadTask.UploadStatus = UploadStatus.Error; 764 uploadTask.UploadDataCompleted?.Invoke(uploadTask); 765 766 if (uploadTask.Group != null && uploadTask.Group.UploadDataCompleted != null) 767 uploadTask.Group.UploadDataCompleted.Invoke(uploadTask.Group); 768 769 lock (locker) 770 { 771 _uploading.Remove(uploadTask); 772 773 if (_uploading.Count < _maximumCount) 774 TryRelease(_maximumCount - _uploading.Count); 775 776 if (uploadTask.Group != null) 777 { 778 uploadTask.Group.UploadDataCompleted?.Invoke(uploadTask.Group); 779 Remove(uploadTask.Group); 780 } 781 } 782 } 783 catch { } 784 } 785 finally 786 { 787 uploadClient.Dispose(); 788 789 if (uploadTask.Error != null) 790 Log.Error(uploadTask.Error, $"上传文件失败,{uploadTask.ErrorMsg},Url:{uploadTask.Url}"); 791 } 792 } 793 794 private void TryRelease(int count = 1) 795 { 796 if (_semaphore == null) 797 return; 798 799 try 800 { 801 while (count-- > 0) 802 { 803 _semaphore.Release(); 804 } 805 } 806 catch { } 807 } 808 809 public void GetCount(out int waitingCount, out int uploadingCount) 810 { 811 lock (locker) 812 { 813 waitingCount = _waiting.Count; 814 uploadingCount = _uploading.Count; 815 } 816 } 817 818 #endregion 819 820 #region Property 821 822 /// <summary> 823 /// 上传器名称 824 /// </summary> 825 public string Name { get; } 826 827 #endregion 828 829 #region Field 830 831 private object locker = new object(); 832 /// <summary> 833 /// 信号量,限制同时上传数 834 /// </summary> 835 private Semaphore _semaphore; 836 private int _maximumCount; 837 /// <summary> 838 /// 等待上传队列 839 /// </summary> 840 private List<UploadTask> _waiting = new List<UploadTask>(); 841 /// <summary> 842 /// 正在上传队列 843 /// </summary> 844 private Dictionary<UploadTask, UploadClientBase> _uploading = new Dictionary<UploadTask, UploadClientBase>(); 845 private Thread _thread; 846 847 #endregion 848 } 849 850 /// <summary> 851 /// 上传任务 852 /// </summary> 853 public sealed class UploadTask : BindableBase, IRetry 854 { 855 private SpeedCalculator _speedCalculator; 856 857 /// <summary> 858 /// 上传任务 859 /// </summary> 860 /// <param name="uploadSpeedEnable">是否计算上传速度</param> 861 public UploadTask(bool uploadSpeedEnable = false) 862 { 863 if (uploadSpeedEnable) 864 { 865 UploadProgressChanged += OnUploadProgressChangedForUploadSpeed; 866 UploadDataCompleted += OnUploadDataCompletedForUploadSpeed; 867 } 868 } 869 870 /// <summary> 871 /// 上传名称 872 /// </summary> 873 public string Name { get; set; } 874 /// <summary> 875 /// 上传编码 876 /// </summary> 877 public string Code { get; set; } 878 /// <summary> 879 /// 额外信息 880 /// </summary> 881 public object Extra { get; set; } 882 /// <summary> 883 /// 额外信息2 884 /// </summary> 885 public object Extra2 { get; set; } 886 /// <summary> 887 /// 额外信息 888 /// </summary> 889 public Dictionary<String, String> Extras { get; set; } 890 /// <summary> 891 /// 上传链接 892 /// </summary> 893 public string Url { get; set; } 894 /// <summary> 895 /// 远程主机 896 /// </summary> 897 public string Host { get; set; } 898 /// <summary> 899 /// 端口号 900 /// </summary> 901 public int Port { get; set; } 902 /// <summary> 903 /// 用户名 904 /// </summary> 905 public string UserName { get; set; } 906 /// <summary> 907 /// 密码 908 /// </summary> 909 public string Password { get; set; } 910 /// <summary> 911 /// 文件流 912 /// </summary> 913 public Stream Stream { get; set; } 914 /// <summary> 915 /// 要上传的文件 916 /// </summary> 917 public string FilePath { get; set; } 918 /// <summary> 919 /// 请求头 920 /// </summary> 921 public Dictionary<String, String> Headers { get; set; } 922 923 /// <summary> 924 /// 上传进度改变或上传完成 925 /// </summary> 926 public Action<UploadTask> UploadProgressChanged { get; set; } 927 /// <summary> 928 /// 不触发事件的取消下载 929 /// </summary> 930 public Action<UploadTask> Removed { get; set; } 931 932 /// <summary> 933 /// 进度百分比 934 /// </summary> 935 public int ProgressPercentage { get; set; } 936 /// <summary> 937 /// 总字节数 938 /// </summary> 939 public long TotalBytesToSend { get; set; } 940 /// <summary> 941 /// 已发送字节数 942 /// </summary> 943 public long BytesSent { get; set; } 944 /// <summary> 945 /// 上传速度 B/s 946 /// </summary> 947 public long? UploadSpeed { get; private set; } 948 /// <summary> 949 /// 不显示下载速度 950 /// </summary> 951 public bool NoSpeed { get; set; } 952 /// <summary> 953 /// 固定百分比 954 /// </summary> 955 public double FixedPercentage { get; set; } 956 /// <summary> 957 /// 剩余时间(秒) 958 /// </summary> 959 public Double TimeRemaining { get; set; } 960 /// <summary> 961 /// 是否分包上传 962 /// </summary> 963 public bool IsPackage { get; set; } 964 /// <summary> 965 /// 分包字节偏移量 966 /// </summary> 967 public long PackageOffset { get; set; } 968 /// <summary> 969 /// 分包大小 970 /// </summary> 971 public long PackageSize { get; set; } 972 /// <summary> 973 /// 分包序号(从1开始) 974 /// </summary> 975 public int PackageIndex { get; set; } 976 /// <summary> 977 /// 分包总数 978 /// </summary> 979 public int PackageCount { get; set; } 980 981 /// <summary> 982 /// 上传完成或上传失败或上传取消 983 /// </summary> 984 public Action<UploadTask> UploadDataCompleted { get; set; } 985 986 /// <summary> 987 /// 是否已取消 988 /// </summary> 989 public bool Cancelled { get; set; } 990 /// <summary> 991 /// 发生的异常 992 /// </summary> 993 public Exception Error { get; set; } 994 /// <summary> 995 /// 错误消息 996 /// </summary> 997 public string ErrorMsg { get; set; } 998 999 public string Response { get; set; } 1000 1001 /// <summary> 1002 /// 上传状态 1003 /// </summary> 1004 public UploadStatus UploadStatus { get; set; } 1005 1006 /// <summary> 1007 /// 所在上传任务组 1008 /// </summary> 1009 public UploadTaskGroup Group { get; set; } 1010 1011 /// <summary> 1012 /// 所在上传器 1013 /// </summary> 1014 public UploadEngine UploadEngine { get; set; } 1015 1016 /// <summary> 1017 /// 上传前触发 1018 /// </summary> 1019 public Func<UploadTask, ResultValue> PreviewUpload { get; set; } 1020 /// <summary> 1021 /// 上传中触发 1022 /// </summary> 1023 public Func<UploadTask, ResultValue> Uploading { get; set; } 1024 1025 /// <summary> 1026 /// 自定义上传方式 1027 /// </summary> 1028 public Func<UploadClientBase> CreateUploadClient { get; set; } 1029 1030 public Action<object> Retrying { get; set; } 1031 1032 /// <summary> 1033 /// 开始时间 1034 /// </summary> 1035 public DateTime? BeginTime { get; set; } 1036 /// <summary> 1037 /// 结束时间 1038 /// </summary> 1039 public DateTime? EndTime { get; set; } 1040 /// <summary> 1041 /// 上传耗时 1042 /// </summary> 1043 public TimeSpan? Duration => EndTime - BeginTime; 1044 /// <summary> 1045 /// 平均上传速度 1046 /// </summary> 1047 public long? AverageUploadSpeed => EndTime.HasValue ? ((long?)(BytesSent / Duration?.TotalSeconds)) : null; 1048 1049 /// <summary> 1050 /// 上传完成标识 1051 /// </summary> 1052 public bool CompletedFlag { get; set; } 1053 1054 private void OnUploadProgressChangedForUploadSpeed(UploadTask obj) 1055 { 1056 if (UploadStatus == UploadStatus.Uploading && _speedCalculator == null && !NoSpeed) 1057 { 1058 _speedCalculator = new SpeedCalculator(() => BytesSent, n => 1059 { 1060 if (UploadStatus != UploadStatus.Uploading) 1061 { 1062 if (_speedCalculator != null) 1063 { 1064 _speedCalculator.Dispose(); 1065 _speedCalculator = null; 1066 } 1067 } 1068 1069 UploadSpeed = n; 1070 if (n == 0) 1071 { 1072 TimeRemaining = Double.NaN; 1073 } 1074 else 1075 { 1076 var bytes_remaining = TotalBytesToSend - BytesSent; 1077 TimeRemaining = bytes_remaining / n; 1078 } 1079 UploadProgressChanged?.Invoke(this); 1080 }); 1081 _speedCalculator.Start(BytesSent); 1082 } 1083 1084 RaisePropertyChanged(null); 1085 } 1086 1087 private void OnUploadDataCompletedForUploadSpeed(UploadTask obj) 1088 { 1089 if (_speedCalculator != null) 1090 { 1091 _speedCalculator.Dispose(); 1092 _speedCalculator = null; 1093 } 1094 1095 RaisePropertyChanged(null); 1096 } 1097 1098 public void Retry() 1099 { 1100 UploadStatus = UploadStatus.Waiting; 1101 BytesSent = 0; 1102 TotalBytesToSend = 0; 1103 ProgressPercentage = 0; 1104 Error = null; 1105 ErrorMsg = null; 1106 Cancelled = false; 1107 1108 Retrying?.Invoke(this); 1109 RaisePropertyChanged(null); 1110 UploadEngine.Add(this); 1111 } 1112 1113 public override string ToString() 1114 { 1115 var sb = new StringBuilder(nameof(UploadTask)); 1116 1117 if (!String.IsNullOrEmpty(Name)) 1118 { 1119 sb.Append(", name: " + Name); 1120 } 1121 if (!String.IsNullOrEmpty(Url)) 1122 { 1123 sb.Append(", url: " + Url); 1124 } 1125 if (!String.IsNullOrEmpty(Host)) 1126 { 1127 sb.Append(", host: " + Host); 1128 } 1129 if (Port != 0) 1130 { 1131 sb.Append(", port: " + Port); 1132 } 1133 if (!String.IsNullOrEmpty(UserName)) 1134 { 1135 sb.Append(", userName: " + UserName); 1136 } 1137 if (!String.IsNullOrEmpty(Password)) 1138 { 1139 sb.Append(", password: " + Password); 1140 } 1141 if (!String.IsNullOrEmpty(FilePath)) 1142 { 1143 sb.Append(", filePath: " + FilePath); 1144 } 1145 sb.Append($", uploadStatus: {UploadStatus}。"); 1146 1147 return sb.ToString(); 1148 } 1149 } 1150 1151 /// <summary> 1152 /// 上传任务组 1153 /// </summary> 1154 public sealed class UploadTaskGroup : BindableBase, IRetry 1155 { 1156 private SpeedCalculator _speedCalculator; 1157 1158 /// <summary> 1159 /// 上传任务组 1160 /// </summary> 1161 /// <param name="uploadTasks">子上传任务</param> 1162 public UploadTaskGroup(IEnumerable<UploadTask> uploadTasks, bool uploadSpeedEnable = false) 1163 { 1164 Children = new List<UploadTask>(uploadTasks); 1165 1166 foreach (var uploadTask in uploadTasks) 1167 { 1168 uploadTask.Group = this; 1169 } 1170 1171 if (uploadSpeedEnable) 1172 { 1173 UploadProgressChanged += OnUploadProgressChangedForUploadSpeed; 1174 UploadDataCompleted += OnUploadDataCompletedForUploadSpeed; 1175 } 1176 } 1177 1178 /// <summary> 1179 /// 子上传任务 1180 /// </summary> 1181 public List<UploadTask> Children { get; } 1182 /// <summary> 1183 /// 追加子任务 1184 /// </summary> 1185 public Action<UploadTask> Appended { get; set; } 1186 1187 /// <summary> 1188 /// 上传名称 1189 /// </summary> 1190 public string Name { get; set; } 1191 /// <summary> 1192 /// 上传编码 1193 /// </summary> 1194 public string Code { get; set; } 1195 /// <summary> 1196 /// 额外信息 1197 /// </summary> 1198 public object Extra { get; set; } 1199 1200 /// <summary> 1201 /// 上传进度改变或上传完成 1202 /// </summary> 1203 public Action<UploadTaskGroup> UploadProgressChanged { get; set; } 1204 1205 /// <summary> 1206 /// 进度百分比 1207 /// </summary> 1208 public int ProgressPercentage 1209 { 1210 get 1211 { 1212 var fixedSum = Children.Sum(q => q.FixedPercentage) / 100; 1213 var totalBytes = (long)(TotalBytesToSend / (1 - fixedSum)); 1214 1215 var sent = 0d; 1216 foreach (var child in Children) 1217 { 1218 if (child.FixedPercentage == 0) 1219 { 1220 sent += child.BytesSent; 1221 } 1222 else 1223 { 1224 switch (child.UploadStatus) 1225 { 1226 case UploadStatus.Completed: 1227 sent += totalBytes * child.FixedPercentage / 100; 1228 break; 1229 case UploadStatus.Uploading: 1230 sent += totalBytes * child.FixedPercentage / 100 * child.ProgressPercentage / 100; 1231 break; 1232 } 1233 } 1234 } 1235 1236 return totalBytes == 0 ? 0 : (int)(sent * 100 / totalBytes); 1237 } 1238 } 1239 /// <summary> 1240 /// 总字节数 1241 /// </summary> 1242 public long TotalBytesToSend => Children.Where(q => q.FixedPercentage == 0).Sum(q => q.TotalBytesToSend); 1243 /// <summary> 1244 /// 已上传字节数 1245 /// </summary> 1246 public long BytesSent => Children.Where(q => q.FixedPercentage == 0).Sum(q => q.BytesSent); 1247 /// <summary> 1248 /// 上传速度 B/s 1249 /// </summary> 1250 public long? UploadSpeed { get; private set; } 1251 /// <summary> 1252 /// 不显示下载速度 1253 /// </summary> 1254 public bool NoSpeed => CurrentUploadTask.NoSpeed; 1255 /// <summary> 1256 /// 剩余时间(秒) 1257 /// </summary> 1258 public Double TimeRemaining { get; set; } 1259 1260 /// <summary> 1261 /// 上传完成或上传失败或上传取消 1262 /// </summary> 1263 public Action<UploadTaskGroup> UploadDataCompleted { get; set; } 1264 1265 /// <summary> 1266 /// 发生的异常 1267 /// </summary> 1268 public Exception Error => Children.FirstOrDefault(q => q.Error != null)?.Error; 1269 /// <summary> 1270 /// 错误消息 1271 /// </summary> 1272 public string ErrorMsg => Children.FirstOrDefault(q => q.Error != null)?.ErrorMsg; 1273 1274 /// <summary> 1275 /// 上传状态 1276 /// </summary> 1277 public UploadStatus UploadStatus 1278 { 1279 get 1280 { 1281 if (Children.Any(q => q.UploadStatus == UploadStatus.Error)) 1282 return UploadStatus.Error; 1283 if (Children.Any(q => q.UploadStatus == UploadStatus.Cancelled)) 1284 return UploadStatus.Cancelled; 1285 if (Children.Any(q => q.UploadStatus == UploadStatus.Uploading)) 1286 return UploadStatus.Uploading; 1287 if (Children.All(q => q.UploadStatus == UploadStatus.Waiting)) 1288 return UploadStatus.Waiting; 1289 if (Children.Any(q => q.UploadStatus == UploadStatus.Waiting)) 1290 return UploadStatus.Uploading; 1291 return UploadStatus.Completed; 1292 } 1293 } 1294 1295 public UploadTask CurrentUploadTask 1296 { 1297 get 1298 { 1299 while (Children.Count > 0) 1300 { 1301 var uploadTask = GetUploadTask(); 1302 if (uploadTask != null) 1303 { 1304 return uploadTask; 1305 } 1306 Thread.Yield(); 1307 } 1308 return null; 1309 } 1310 } 1311 private UploadTask GetUploadTask() 1312 { 1313 var status = UploadStatus; 1314 1315 if (status == UploadStatus.Waiting) 1316 return Children.FirstOrDefault(q => q.UploadStatus == status); 1317 else if (status == UploadStatus.Uploading) 1318 return Children.FirstOrDefault(q => q.UploadStatus == status) ?? Children.FirstOrDefault(q => q.UploadStatus == UploadStatus.Waiting); 1319 else 1320 return Children.LastOrDefault(q => q.UploadStatus == status) ?? Children.LastOrDefault(); 1321 } 1322 1323 /// <summary> 1324 /// 所在上传器 1325 /// </summary> 1326 public UploadEngine UploadEngine { get; set; } 1327 1328 /// <summary> 1329 /// 上传前触发 1330 /// </summary> 1331 public Func<UploadTaskGroup, ResultValue> PreviewUpload { get; set; } 1332 public Action<object> Retrying { get; set; } 1333 1334 /// <summary> 1335 /// 开始时间 1336 /// </summary> 1337 public DateTime? BeginTime => Children.Where(q => q.BeginTime.HasValue).Min(q => q.BeginTime); 1338 /// <summary> 1339 /// 结束时间 1340 /// </summary> 1341 public DateTime? EndTime => Children.Where(q => q.EndTime.HasValue || q.BeginTime.HasValue).Max(q => q.EndTime.HasValue ? q.EndTime : q.BeginTime); 1342 /// <summary> 1343 /// 上传耗时 1344 /// </summary> 1345 public TimeSpan? Duration => EndTime - BeginTime; 1346 /// <summary> 1347 /// 平均上传速度 1348 /// </summary> 1349 public long? AverageUploadSpeed => EndTime.HasValue ? ((long?)(BytesSent / Duration?.TotalSeconds)) : null; 1350 1351 private void OnUploadProgressChangedForUploadSpeed(UploadTaskGroup obj) 1352 { 1353 if (UploadStatus == UploadStatus.Uploading && _speedCalculator == null) 1354 { 1355 _speedCalculator = new SpeedCalculator(() => BytesSent, n => 1356 { 1357 if (UploadStatus != UploadStatus.Uploading) 1358 { 1359 if (_speedCalculator != null) 1360 { 1361 _speedCalculator.Dispose(); 1362 _speedCalculator = null; 1363 } 1364 } 1365 if (NoSpeed) 1366 { 1367 UploadSpeed = null; 1368 TimeRemaining = Double.NaN; 1369 } 1370 else 1371 { 1372 UploadSpeed = n; 1373 if (n == 0) 1374 { 1375 TimeRemaining = Double.NaN; 1376 } 1377 else 1378 { 1379 var bytes_remaining = TotalBytesToSend - BytesSent; 1380 TimeRemaining = bytes_remaining / n; 1381 } 1382 } 1383 1384 UploadProgressChanged?.Invoke(this); 1385 }); 1386 _speedCalculator.Start(BytesSent); 1387 } 1388 1389 RaisePropertyChanged(null); 1390 } 1391 1392 private void OnUploadDataCompletedForUploadSpeed(UploadTaskGroup obj) 1393 { 1394 if (_speedCalculator != null) 1395 { 1396 _speedCalculator.Dispose(); 1397 _speedCalculator = null; 1398 } 1399 1400 RaisePropertyChanged(null); 1401 } 1402 1403 public void Retry() 1404 { 1405 foreach (var child in Children) 1406 { 1407 child.UploadStatus = UploadStatus.Waiting; 1408 child.BytesSent = 0; 1409 child.TotalBytesToSend = 0; 1410 child.ProgressPercentage = 0; 1411 child.Error = null; 1412 child.ErrorMsg = null; 1413 child.Cancelled = false; 1414 1415 child.Retrying?.Invoke(child); 1416 child.RaisePropertyChanged(null); 1417 } 1418 1419 Retrying?.Invoke(this); 1420 RaisePropertyChanged(null); 1421 UploadEngine.Add(this); 1422 } 1423 1424 public override string ToString() 1425 { 1426 var sb = new StringBuilder(nameof(UploadTaskGroup)); 1427 1428 if (!String.IsNullOrEmpty(Name)) 1429 { 1430 sb.Append(", name: " + Name); 1431 } 1432 sb.Append($", uploadStatus: {UploadStatus}。"); 1433 1434 return sb.ToString(); 1435 } 1436 } 1437 1438 /// <summary> 1439 /// 上传进度监测器 1440 /// </summary> 1441 public sealed class UploadProgressWatcher : BindableBase, IDisposable 1442 { 1443 private SpeedCalculator _speedCalculator; 1444 private readonly List<UploadTask> _uploadTasks = new List<UploadTask>(); 1445 1446 public UploadProgressWatcher() 1447 { 1448 UploadProgressChanged += OnUploadProgressChangedForUploadSpeed; 1449 UploadDataCompleted += OnUploadFileCompletedForUploadSpeed; 1450 } 1451 1452 public void AppendUploadTask(UploadTask uploadTask) 1453 { 1454 if (_uploadTasks.Contains(uploadTask)) 1455 { 1456 return; 1457 } 1458 1459 _uploadTasks.Add(uploadTask); 1460 1461 if (uploadTask.TotalBytesToSend == 0 && uploadTask != CurrentUploadTask && uploadTask.PreviewUpload != null) 1462 { 1463 var ret = uploadTask.PreviewUpload.Invoke(uploadTask); 1464 1465 if (!ret.IsSucc) 1466 { 1467 _uploadTasks.Remove(uploadTask); 1468 return; 1469 } 1470 } 1471 1472 UploadProgressChanged?.Invoke(this); 1473 1474 uploadTask.UploadProgressChanged -= OnUploadTaskUploadProgressChanged; 1475 uploadTask.UploadProgressChanged += OnUploadTaskUploadProgressChanged; 1476 uploadTask.UploadDataCompleted -= OnUploadTaskUploadDataCompleted; 1477 uploadTask.UploadDataCompleted += OnUploadTaskUploadDataCompleted; 1478 uploadTask.Removed -= OnUploadTaskRemoved; 1479 uploadTask.Removed += OnUploadTaskRemoved; 1480 } 1481 1482 public void AppendUploadTaskGroup(UploadTaskGroup group) 1483 { 1484 foreach (var uploadTask in group.Children) 1485 { 1486 AppendUploadTask(uploadTask); 1487 } 1488 1489 group.Appended -= AppendUploadTask; 1490 group.Appended += AppendUploadTask; 1491 group.UploadDataCompleted -= OnUploadTaskGroupUploadDataCompleted; 1492 group.UploadDataCompleted += OnUploadTaskGroupUploadDataCompleted; 1493 } 1494 1495 1496 public UploadTask[] UploadTasks => _uploadTasks.ToArray(); 1497 /// <summary> 1498 /// 上传进度改变或上传完成 1499 /// </summary> 1500 public Action<UploadProgressWatcher> UploadProgressChanged { get; set; } 1501 1502 /// <summary> 1503 /// 进度百分比 1504 /// </summary> 1505 public int ProgressPercentage 1506 { 1507 get 1508 { 1509 var fixedSum = _uploadTasks.Sum(q => q.FixedPercentage) / 100 / _uploadTasks.Count; 1510 var totalBytes = (long)(TotalBytesToSend / (1 - fixedSum)); 1511 1512 var sent = 0d; 1513 1514 foreach (var child in _uploadTasks) 1515 { 1516 if (child.FixedPercentage == 0) 1517 { 1518 sent += child.BytesSent; 1519 } 1520 else 1521 { 1522 switch (child.UploadStatus) 1523 { 1524 case UploadStatus.Completed: 1525 sent += totalBytes * child.FixedPercentage / 100 / _uploadTasks.Count; 1526 break; 1527 case UploadStatus.Uploading: 1528 sent += totalBytes * child.FixedPercentage / 100 / _uploadTasks.Count * child.ProgressPercentage / 100; 1529 break; 1530 } 1531 } 1532 } 1533 1534 return totalBytes == 0 ? 0 : (int)(sent * 100 / totalBytes); 1535 } 1536 } 1537 /// <summary> 1538 /// 总字节数 1539 /// </summary> 1540 public long TotalBytesToSend => _uploadTasks.Where(q => q.FixedPercentage == 0).Sum(q => q.TotalBytesToSend); 1541 /// <summary> 1542 /// 已上传字节数 1543 /// </summary> 1544 public long BytesSent => _uploadTasks.Where(q => q.FixedPercentage == 0).Sum(q => q.BytesSent); 1545 /// <summary> 1546 /// 上传速度 B/s 1547 /// </summary> 1548 public long? UploadSpeed { get; private set; } 1549 1550 /// <summary> 1551 /// 上传完成或上传失败或上传取消 1552 /// </summary> 1553 public Action<UploadProgressWatcher> UploadDataCompleted { get; set; } 1554 1555 /// <summary> 1556 /// 上传状态 1557 /// </summary> 1558 public UploadStatus UploadStatus 1559 { 1560 get 1561 { 1562 var tasks = _uploadTasks.Where(q => q.UploadStatus != UploadStatus.Cancelled && q.UploadStatus != UploadStatus.Error).ToArray(); 1563 1564 if (tasks.Any(q => q.UploadStatus == UploadStatus.Uploading)) 1565 return UploadStatus.Uploading; 1566 if (tasks.All(q => q.UploadStatus == UploadStatus.Waiting)) 1567 return UploadStatus.Waiting; 1568 else 1569 return UploadStatus.Completed; 1570 } 1571 } 1572 1573 public UploadTask CurrentUploadTask 1574 { 1575 get 1576 { 1577 while (_uploadTasks.Count > 0) 1578 { 1579 var uploadTask = GetUploadTask(); 1580 if (uploadTask != null) 1581 { 1582 return uploadTask; 1583 } 1584 Thread.Yield(); 1585 } 1586 return null; 1587 } 1588 } 1589 private UploadTask GetUploadTask() 1590 { 1591 var status = UploadStatus; 1592 1593 if (status == UploadStatus.Waiting) 1594 return _uploadTasks.FirstOrDefault(q => q.UploadStatus == status); 1595 else if (status == UploadStatus.Uploading) 1596 return _uploadTasks.FirstOrDefault(q => q.UploadStatus == status) ?? _uploadTasks.FirstOrDefault(q => q.UploadStatus == UploadStatus.Waiting); 1597 else 1598 return _uploadTasks.LastOrDefault(q => q.UploadStatus == status) ?? _uploadTasks.LastOrDefault(); 1599 } 1600 1601 private void OnUploadTaskUploadProgressChanged(UploadTask uploadTask) 1602 { 1603 UploadProgressChanged?.Invoke(this); 1604 } 1605 1606 private void OnUploadTaskUploadDataCompleted(UploadTask uploadTask) 1607 { 1608 uploadTask.UploadDataCompleted -= OnUploadTaskUploadDataCompleted; 1609 1610 if (_uploadTasks.All(q => q.UploadStatus == UploadStatus.Completed || q.UploadStatus == UploadStatus.Cancelled || q.UploadStatus == UploadStatus.Error)) 1611 { 1612 UploadDataCompleted?.Invoke(this); 1613 } 1614 } 1615 1616 private void OnUploadTaskRemoved(UploadTask uploadTask) 1617 { 1618 uploadTask.Removed -= OnUploadTaskRemoved; 1619 _uploadTasks.Remove(uploadTask); 1620 1621 UploadProgressChanged?.Invoke(this); 1622 } 1623 1624 private void OnUploadTaskGroupUploadDataCompleted(UploadTaskGroup group) 1625 { 1626 group.UploadDataCompleted -= OnUploadTaskGroupUploadDataCompleted; 1627 group.Appended -= AppendUploadTask; 1628 } 1629 1630 private void OnUploadProgressChangedForUploadSpeed(UploadProgressWatcher watcher) 1631 { 1632 if (UploadStatus == UploadStatus.Uploading && _speedCalculator == null && BytesSent > 0) 1633 { 1634 _speedCalculator = new SpeedCalculator(() => BytesSent, n => 1635 { 1636 if (UploadStatus != UploadStatus.Uploading) 1637 { 1638 if (_speedCalculator != null) 1639 { 1640 _speedCalculator.Dispose(); 1641 _speedCalculator = null; 1642 } 1643 } 1644 1645 UploadSpeed = n; 1646 UploadProgressChanged?.Invoke(this); 1647 }); 1648 _speedCalculator.Start(BytesSent); 1649 } 1650 1651 RaisePropertyChanged(null); 1652 } 1653 1654 private void OnUploadFileCompletedForUploadSpeed(UploadProgressWatcher watcher) 1655 { 1656 if (_speedCalculator != null) 1657 { 1658 _speedCalculator.Dispose(); 1659 _speedCalculator = null; 1660 } 1661 1662 RaisePropertyChanged(null); 1663 } 1664 1665 public void Dispose() 1666 { 1667 if (_speedCalculator != null) 1668 { 1669 _speedCalculator.Dispose(); 1670 _speedCalculator = null; 1671 } 1672 1673 foreach (var uploadTask in _uploadTasks) 1674 { 1675 uploadTask.UploadProgressChanged -= OnUploadTaskUploadProgressChanged; 1676 uploadTask.UploadDataCompleted -= OnUploadTaskUploadDataCompleted; 1677 uploadTask.Removed -= OnUploadTaskRemoved; 1678 1679 var group = uploadTask.Group; 1680 1681 if (group != null) 1682 { 1683 group.Appended -= AppendUploadTask; 1684 group.UploadDataCompleted -= OnUploadTaskGroupUploadDataCompleted; 1685 } 1686 } 1687 } 1688 } 1689 1690 public abstract class UploadClientBase : IDisposable 1691 { 1692 public UploadClientBase(UploadTask uploadTask) 1693 { 1694 _uploadTask = uploadTask; 1695 } 1696 1697 public abstract void BeginUpload(); 1698 1699 public abstract void BeginCancel(); 1700 1701 public abstract void Dispose(); 1702 1703 #region Property 1704 1705 public UploadTask UploadTask => _uploadTask; 1706 1707 public Action<(UploadClientBase sender, long BytesSent, long TotalBytesToSend, int ProgressPercentage)> UploadProgressChanged { get; set; } 1708 public Action<(UploadClientBase sender, Exception Error, bool Cancelled)> UploadDataCompleted { get; set; } 1709 1710 #endregion 1711 1712 #region Field 1713 1714 private UploadTask _uploadTask; 1715 1716 #endregion 1717 } 1718 1719 public class HttpUploadClient : UploadClientBase 1720 { 1721 public HttpUploadClient(UploadTask uploadTask) : base(uploadTask) 1722 { 1723 _request = (HttpWebRequest)WebRequest.Create(uploadTask.Url); 1724 1725 _request.Method = "POST"; 1726 _request.UseDefaultCredentials = true; 1727 _request.Timeout = 60_000 * 60; 1728 _request.KeepAlive = true; 1729 } 1730 1731 public override void BeginUpload() 1732 { 1733 _cts.TryCancel(); 1734 _cts = new CancellationTokenSource(); 1735 1736 ThreadPool.QueueUserWorkItem(n => 1737 { 1738 var token = (CancellationToken)n; 1739 1740 try 1741 { 1742 if (UploadTask.Uploading != null) 1743 { 1744 var ret = UploadTask.Uploading.Invoke(UploadTask); 1745 1746 if (token.IsCancellationRequested) 1747 { 1748 UploadDataCompleted?.Invoke((this, null, token.IsCancellationRequested)); 1749 return; 1750 } 1751 1752 if (!ret.IsSucc) 1753 { 1754 UploadDataCompleted?.Invoke((this, ret.Error, token.IsCancellationRequested)); 1755 } 1756 } 1757 1758 var uri = new Uri(UploadTask.Url, UriKind.RelativeOrAbsolute); 1759 1760 if ((UploadTask.Stream == null || !UploadTask.Stream.CanRead) && File.Exists(UploadTask.FilePath)) 1761 UploadTask.Stream = File.OpenRead(UploadTask.FilePath); 1762 1763 if (UploadTask.Headers != null) 1764 { 1765 foreach (var kv in UploadTask.Headers) 1766 { 1767 if ("Content-Type".Equals(kv.Key, StringComparison.OrdinalIgnoreCase)) 1768 { 1769 _request.ContentType = kv.Value; 1770 continue; 1771 } 1772 1773 _request.Headers.Set(kv.Key, kv.Value); 1774 } 1775 } 1776 _request.ContentLength = UploadTask.Stream.Length; 1777 1778 using (var stream = UploadTask.Stream) 1779 { 1780 using (var reqStream = _request.GetRequestStream()) 1781 { 1782 if (stream.CanSeek) 1783 stream.Seek(0, SeekOrigin.Begin); 1784 1785 var buffLength = 1024; 1786 var buff = new byte[buffLength]; 1787 1788 var contentLen = 0; 1789 UploadTask.BytesSent = 0L; 1790 1791 do 1792 { 1793 contentLen = stream.Read(buff, 0, buffLength); 1794 reqStream.Write(buff, 0, contentLen); 1795 1796 UploadTask.BytesSent += contentLen; 1797 1798 UploadProgressChanged?.Invoke((this, UploadTask.BytesSent, stream.Length, (int)(UploadTask.BytesSent * 100d / stream.Length))); 1799 } 1800 while (contentLen != 0 && !token.IsCancellationRequested); 1801 1802 reqStream.Flush(); 1803 } 1804 } 1805 1806 if (!token.IsCancellationRequested) 1807 { 1808 using (var response = (HttpWebResponse)_request.GetResponse()) 1809 { 1810 using (var sr = new StreamReader(response.GetResponseStream())) 1811 { 1812 UploadTask.Response = sr.ReadToEnd(); 1813 Log.Info($"上传成功,大小:{UploadTask.TotalBytesToSend},上传地址:{UploadTask.Url},{UploadTask.Response}"); 1814 } 1815 } 1816 } 1817 1818 UploadDataCompleted?.Invoke((this, null, token.IsCancellationRequested)); 1819 } 1820 catch (Exception ex) 1821 { 1822 UploadDataCompleted?.Invoke((this, ex, token.IsCancellationRequested)); 1823 } 1824 }, _cts.Token); 1825 } 1826 1827 public override void BeginCancel() 1828 { 1829 if (_cts != null) 1830 { 1831 _cts.TryCancel(); 1832 _cts = null; 1833 } 1834 } 1835 1836 public override void Dispose() 1837 { 1838 _request.Abort(); 1839 1840 if (_cts != null) 1841 { 1842 _cts.TryCancel(); 1843 _cts = null; 1844 } 1845 } 1846 1847 #region Field 1848 1849 private HttpWebRequest _request; 1850 private CancellationTokenSource _cts; 1851 1852 #endregion 1853 } 1854 1855 public class FtpUploadClient : UploadClientBase 1856 { 1857 public FtpUploadClient(UploadTask uploadTask) : base(uploadTask) 1858 { 1859 _ftp = (FtpWebRequest)FtpWebRequest.Create(new Uri(uploadTask.Url, UriKind.RelativeOrAbsolute)); 1860 _ftp.Credentials = new NetworkCredential(uploadTask.UserName, uploadTask.Password); 1861 _ftp.Method = WebRequestMethods.Ftp.UploadFile; 1862 _ftp.UseBinary = true; 1863 } 1864 1865 public override void BeginUpload() 1866 { 1867 _cts?.TryCancel(); 1868 _cts = new CancellationTokenSource(); 1869 1870 ThreadPool.QueueUserWorkItem(n => 1871 { 1872 var token = (CancellationToken)n; 1873 1874 try 1875 { 1876 if ((UploadTask.Stream == null || !UploadTask.Stream.CanRead) && File.Exists(UploadTask.FilePath)) 1877 UploadTask.Stream = File.OpenRead(UploadTask.FilePath); 1878 1879 using (var stream = UploadTask.Stream) 1880 { 1881 using (var reqStream = _ftp.GetRequestStream()) 1882 { 1883 if (stream.CanSeek) 1884 stream.Seek(0, SeekOrigin.Begin); 1885 1886 var buffLength = 1024; 1887 var buff = new byte[buffLength]; 1888 1889 var contentLen = 0; 1890 UploadTask.BytesSent = 0L; 1891 1892 do 1893 { 1894 contentLen = stream.Read(buff, 0, buffLength); 1895 reqStream.Write(buff, 0, contentLen); 1896 1897 UploadTask.BytesSent += contentLen; 1898 1899 UploadProgressChanged?.Invoke((this, UploadTask.BytesSent, stream.Length, (int)(UploadTask.BytesSent * 100d / stream.Length))); 1900 } 1901 while (contentLen != 0 && !token.IsCancellationRequested); 1902 1903 reqStream.Flush(); 1904 } 1905 } 1906 1907 UploadDataCompleted?.Invoke((this, null, token.IsCancellationRequested)); 1908 } 1909 catch (Exception ex) 1910 { 1911 UploadDataCompleted?.Invoke((this, ex, token.IsCancellationRequested)); 1912 } 1913 }, _cts.Token); 1914 } 1915 1916 public override void BeginCancel() 1917 { 1918 if (_cts != null) 1919 { 1920 _cts.TryCancel(); 1921 _cts = null; 1922 } 1923 } 1924 1925 public override void Dispose() 1926 { 1927 _ftp.Abort(); 1928 1929 if (_cts != null) 1930 { 1931 _cts.TryCancel(); 1932 _cts = null; 1933 } 1934 } 1935 1936 #region Field 1937 1938 private FtpWebRequest _ftp; 1939 private CancellationTokenSource _cts; 1940 1941 #endregion 1942 } 1943 1944 public class SftpUploadClient : UploadClientBase 1945 { 1946 public SftpUploadClient(UploadTask uploadTask) : base(uploadTask) 1947 { 1948 _sftp = new SftpClient(uploadTask.Host, uploadTask.Port, uploadTask.UserName, uploadTask.Password); 1949 } 1950 1951 public override void BeginUpload() 1952 { 1953 _cts?.TryCancel(); 1954 _cts = new CancellationTokenSource(); 1955 1956 ThreadPool.QueueUserWorkItem(n => 1957 { 1958 var token = (CancellationToken)n; 1959 1960 try 1961 { 1962 _sftp.Connect(); 1963 1964 if ((UploadTask.Stream == null || !UploadTask.Stream.CanRead) && File.Exists(UploadTask.FilePath)) 1965 UploadTask.Stream = File.OpenRead(UploadTask.FilePath); 1966 1967 using (var stream = UploadTask.Stream) 1968 { 1969 if (stream.CanSeek) 1970 stream.Seek(0, SeekOrigin.Begin); 1971 1972 _sftp.UploadFile(stream, UploadTask.Url, m => { UploadTask.BytesSent = (long)m; UploadProgressChanged?.Invoke((this, UploadTask.BytesSent, UploadTask.TotalBytesToSend, (int)(m * 100d / UploadTask.TotalBytesToSend))); }); 1973 1974 if (UploadTask.BytesSent != UploadTask.TotalBytesToSend) 1975 { 1976 UploadTask.BytesSent = UploadTask.TotalBytesToSend; 1977 UploadProgressChanged?.Invoke((this, UploadTask.BytesSent, UploadTask.TotalBytesToSend, 100)); 1978 } 1979 } 1980 1981 UploadDataCompleted?.Invoke((this, null, token.IsCancellationRequested)); 1982 } 1983 catch (Exception ex) 1984 { 1985 UploadDataCompleted?.Invoke((this, ex, token.IsCancellationRequested)); 1986 } 1987 }, _cts.Token); 1988 } 1989 1990 public override void BeginCancel() 1991 { 1992 if (_cts != null) 1993 { 1994 _cts.TryCancel(); 1995 _cts = null; 1996 } 1997 } 1998 1999 public override void Dispose() 2000 { 2001 _sftp.Dispose(); 2002 2003 if (_cts != null) 2004 { 2005 _cts.TryCancel(); 2006 _cts = null; 2007 } 2008 } 2009 2010 #region Field 2011 2012 private SftpClient _sftp; 2013 private CancellationTokenSource _cts; 2014 2015 #endregion 2016 } 2017 2018 public class CustomUploadClient : UploadClientBase 2019 { 2020 public CustomUploadClient(UploadTask uploadTask) : base(uploadTask) { } 2021 2022 public override void BeginUpload() 2023 { 2024 if (BeginUploadCalled == null) 2025 { 2026 UploadTask.UploadStatus = UploadStatus.Completed; 2027 UploadDataCompleted?.Invoke((this, null, false)); 2028 } 2029 else 2030 { 2031 _cts?.TryCancel(); 2032 _cts = new CancellationTokenSource(); 2033 BeginUploadCalled.Invoke(this, _cts.Token); 2034 } 2035 } 2036 2037 public override void BeginCancel() 2038 { 2039 if (_cts != null) 2040 { 2041 _cts.TryCancel(); 2042 _cts = null; 2043 } 2044 2045 if (BeginCancelCalled != null) 2046 { 2047 BeginCancelCalled.Invoke(this); 2048 } 2049 else 2050 { 2051 UploadDataCompleted?.Invoke((this, null, true)); 2052 } 2053 } 2054 2055 public override void Dispose() 2056 { 2057 if (_cts != null) 2058 { 2059 _cts.TryCancel(); 2060 _cts = null; 2061 } 2062 2063 DisposeCalled?.Invoke(this); 2064 } 2065 2066 #region Property 2067 2068 public Action<CustomUploadClient, CancellationToken> BeginUploadCalled { get; set; } 2069 2070 public Action<CustomUploadClient> BeginCancelCalled { get; set; } 2071 2072 public Action<CustomUploadClient> DisposeCalled { get; set; } 2073 2074 #endregion 2075 2076 #region Field 2077 2078 private CancellationTokenSource _cts; 2079 2080 #endregion 2081 } 2082 }
分类:
C#
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本