From c55e5c8f1f0b71c8c34a2d93e503403826967f5e Mon Sep 17 00:00:00 2001 From: CWX Date: Tue, 7 Jun 2022 20:34:09 +0100 Subject: [PATCH] update to show progress for download --- Aki.Core/Program.cs | 4 +- Aki.Helper/DownloadHelper.cs | 42 ++++++++++------ Aki.Helper/HttpClientProgressExtensions.cs | 57 ++++++++++++++++++++++ 3 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 Aki.Helper/HttpClientProgressExtensions.cs diff --git a/Aki.Core/Program.cs b/Aki.Core/Program.cs index 77ac7ba..c802499 100644 --- a/Aki.Core/Program.cs +++ b/Aki.Core/Program.cs @@ -55,7 +55,7 @@ namespace SPT_AKI_Installer.Aki.Core if (patcherZipPath == null && DownloadHelper.patchNeedCheck) { LogHelper.Info("No Patcher zip file present in directory, downloading..."); - var task = DownloadHelper.DownloadFileAsync(targetPath, DownloadHelper.patcherLink, "/PATCHERZIP.zip"); + var task = DownloadHelper.DownloadFile(targetPath, DownloadHelper.patcherLink, "/PATCHERZIP.zip"); while(task.Status != System.Threading.Tasks.TaskStatus.RanToCompletion) { } @@ -65,7 +65,7 @@ namespace SPT_AKI_Installer.Aki.Core if (akiZipPath == null) { LogHelper.Info("No AKI zip file present in directory, downloading..."); - var task = DownloadHelper.DownloadFileAsync(targetPath, DownloadHelper.akiLink, "/AKIZIP.zip"); + var task = DownloadHelper.DownloadFile(targetPath, DownloadHelper.akiLink, "/AKIZIP.zip"); while (task.Status != System.Threading.Tasks.TaskStatus.RanToCompletion) { } diff --git a/Aki.Helper/DownloadHelper.cs b/Aki.Helper/DownloadHelper.cs index 611110d..3f2ca94 100644 --- a/Aki.Helper/DownloadHelper.cs +++ b/Aki.Helper/DownloadHelper.cs @@ -3,10 +3,12 @@ using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; +using System.Threading; using Gitea.Api; using Gitea.Client; using Gitea.Model; -using System.Collections.Generic; +using Spectre.Console; +using HttpClientProgress; namespace SPT_AKI_Installer.Aki.Helper { @@ -16,14 +18,6 @@ namespace SPT_AKI_Installer.Aki.Helper public static string akiLink; public static string patcherLink; - public static async Task DownloadFileAsync(string targetFilePath, string targetLink, string newFileName) - { - using (var httpClient = new HttpClient()) - { - await httpClient.DownloadFile(targetLink, Path.Join(targetFilePath, newFileName)); - } - } - public static async Task ReleaseCheck() { Configuration.Default.BasePath = "https://dev.sp-tarkov.com/api/v1"; @@ -79,14 +73,32 @@ namespace SPT_AKI_Installer.Aki.Helper } } - public static async Task DownloadFile(this HttpClient client, string address, string fileName) + public static async Task DownloadFile(string targetFilePath, string targetLink, string newFileName) { - using (var response = await client.GetAsync(address)) - using (var stream = await response.Content.ReadAsStreamAsync()) - using (var file = File.OpenWrite(fileName)) + await AnsiConsole.Progress().Columns( + new PercentageColumn(), + new TaskDescriptionColumn(), + new ProgressBarColumn(), + new ElapsedTimeColumn(), + new SpinnerColumn() + ).StartAsync(async (ProgressContext context) => { - stream.CopyTo(file); - } + var task = context.AddTask("Downloading File"); + + var client = new HttpClient(); + var docUrl = targetLink; + var filePath = Path.Join(targetFilePath, newFileName); + + // Setup your progress reporter + var progress = new Progress((float progress) => + { + task.Value = progress; + }); + + // Use the provided extension method + using (var file = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) + await client.DownloadDataAsync(docUrl, file, progress); + }); } } } diff --git a/Aki.Helper/HttpClientProgressExtensions.cs b/Aki.Helper/HttpClientProgressExtensions.cs new file mode 100644 index 0000000..32c2c41 --- /dev/null +++ b/Aki.Helper/HttpClientProgressExtensions.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace HttpClientProgress +{ + public static class HttpClientProgressExtensions + { + public static async Task DownloadDataAsync(this HttpClient client, string requestUrl, Stream destination, IProgress progress = null, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var response = await client.GetAsync(requestUrl, HttpCompletionOption.ResponseHeadersRead)) + { + var contentLength = response.Content.Headers.ContentLength; + using (var download = await response.Content.ReadAsStreamAsync()) + { + // no progress... no contentLength... very sad + if (progress is null || !contentLength.HasValue) + { + await download.CopyToAsync(destination); + return; + } + // Such progress and contentLength much reporting Wow! + var progressWrapper = new Progress(totalBytes => progress.Report(GetProgressPercentage(totalBytes, contentLength.Value))); + await download.CopyToAsync(destination, 81920, progressWrapper, cancellationToken); + } + } + + float GetProgressPercentage(float totalBytes, float currentBytes) => (totalBytes / currentBytes) * 100f; + } + + static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress progress = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (bufferSize < 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize)); + if (source is null) + throw new ArgumentNullException(nameof(source)); + if (!source.CanRead) + throw new InvalidOperationException($"'{nameof(source)}' is not readable."); + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + if (!destination.CanWrite) + throw new InvalidOperationException($"'{nameof(destination)}' is not writable."); + + var buffer = new byte[bufferSize]; + long totalBytesRead = 0; + int bytesRead; + while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); + totalBytesRead += bytesRead; + progress?.Report(totalBytesRead); + } + } + } +} \ No newline at end of file