/// <summary> /// 开始下载文件 /// </summary> /// <returns></returns> public async Task DownloadFile() { _logger.LogInformation($"Start download Url={Url} File={File.FullName}"); var(response, contentLength) = await GetContentLength(); FileStream = File.Create(); FileStream.SetLength(contentLength); FileWriter = new RandomFileWriter(FileStream); SegmentManager = new SegmentManager(contentLength); _progress.Report(new DownloadProgress($"file length = {contentLength}", SegmentManager)); var downloadSegment = SegmentManager.GetNewDownloadSegment(); // 下载第一段 Download(response, downloadSegment); var supportSegment = await TryDownloadLast(contentLength); var threadCount = 1; if (supportSegment) { // 多创建几个线程下载 threadCount = 10; for (var i = 0; i < threadCount; i++) { Download(SegmentManager.GetNewDownloadSegment()); } } for (var i = 0; i < threadCount; i++) { _ = Task.Run(DownloadTask); } await FileDownloadTask.Task; }
/// <summary> /// 网速控制开关 /// </summary> /// <returns></returns> private async void ControlSwitch() { // 使用独立的线程的优势在于不需要等待下载就能进入方法 // 可以干掉 LastTime 属性,因此定时是 3 秒 await Task.Delay(ControlDelayTime); while (!SegmentManager.IsFinished()) { LogDebugInternal("Start ControlSwitch"); var(segment, runCount, maxReportTime) = SegmentManager.GetDownloadSegmentStatus(); int waitCount = DownloadDataList.Count; LogDebugInternal("ControlSwitch 当前等待数量:{0},待命最大响应时间:{1},运行数量:{2},运行线程{3}", waitCount, maxReportTime, runCount, _threadCount); if (maxReportTime > TimeSpan.FromSeconds(10) && segment != null && runCount > 1) { // 此时速度太慢 segment.LoadingState = DownloadingState.Pause; LogDebugInternal("ControlSwitch slowly pause segment={0}", segment.Number); } else if (maxReportTime < TimeSpan.FromMilliseconds(600) && waitCount > 0 || runCount < 1) { // 速度非常快,尝试再开线程,或者当前没有在进行的任务 // 如果此时是刚好全部完成了,而 runCount 是 0 进入 StartDownloadTask 也将会啥都不做 LogDebugInternal("ControlSwitch StartDownloadTask"); // 这里不需要线程安全,如果刚好全部线程都在退出,等待 ControlDelayTime 再次创建 if (_threadCount < MaxThreadCount) { StartDownloadTask(); } } LogDebugInternal("Finish ControlSwitch"); //变速器3秒为一周期 await Task.Delay(ControlDelayTime); } }
/// <summary> /// 开始下载文件 /// </summary> /// <returns></returns> public async Task DownloadFileAsync() { _logger.LogInformation($"Start download Url={Url} File={File.FullName}"); var(response, contentLength) = await GetContentLength(); _logger.LogInformation($"ContentLength={contentLength}"); if (contentLength < 0) { // contentLength == -1 // 当前非下载内容,没有存在长度 // 可测试使用的链接是 https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.100-preview.7-windows-x64-installer _logger.LogWarning($"Can not download file. ContentLength={contentLength}"); return; } FileStream = File.Create(); FileStream.SetLength(contentLength); FileWriter = new RandomFileWriterWithOrderFirst(FileStream); FileWriter.StepWriteFinished += (sender, args) => SharedArrayPool.Return(args.Data); SegmentManager = new SegmentManager(contentLength); _progress.Report(new DownloadProgress($"file length = {contentLength}", SegmentManager)); var downloadSegment = SegmentManager.GetNewDownloadSegment(); // 下载第一段 Download(response, downloadSegment !); var supportSegment = await TryDownloadLast(contentLength); int threadCount; if (supportSegment) { // 先根据文件的大小,大概是 1M 让一个线程下载,至少需要开两个线程,最多是 10 个线程 threadCount = Math.Max(Math.Min(2, (int)(contentLength / 1024 / 1024)), MaxThreadCount); } else { // 不支持分段下载下,多个线程也没啥用 threadCount = 1; } if (supportSegment) { // 多创建几个线程下载 for (var i = 0; i < threadCount; i++) { Download(SegmentManager.GetNewDownloadSegment()); } //控制开关,如果下载阻塞就先暂停 ControlSwitch(); } // 一开始就创建足够量的线程尝试下载 for (var i = 0; i < threadCount; i++) { StartDownloadTask(); } await FileDownloadTask.Task; }
public DownloadProgress(string message, SegmentManager segmentManager) { Message = message; SegmentManager = segmentManager; }
public DownloadProgress(SegmentManager segmentManager) { SegmentManager = segmentManager; }