[C#] 下载器与上传器使用示例

由于下载器与上传器实现原理相似,所以仅以下载器举例。
优势:
  1. 创建多个下载器,例如负责数据管理模块的、负责专家服务的等;
  2. 同时多个下载任务,例如可设置数据管理同时下载多个,而非逐个排队下载;
  3. 多个下载任务可组合成一个下载任务组,一个下载任务可加入多个组,加入多个组不会重复下载;(加入多个组理论可行,但有些代码要完善。)
  4. 下载任务及下载任务组都有进度值、下载速度,有下载进度改变通知、下载结束通知;
  5. 下载过程中可处理预下载事件,可中途插入或移除下载任务;
  6. 支持自定义的下载方式,只需实现抽象类,目前已提供常规的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 }
View Code
复制代码

 

上传器关键源码:
复制代码
   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 }
View Code
复制代码

 

posted @   孤独成派  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
点击右上角即可分享
微信分享提示