using System; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace FModel.Utils { /// /// https://stackoverflow.com/questions/20661652/progress-bar-with-httpclient /// public class HttpClientDownloadWithProgress : IDisposable { private readonly string _downloadUrl; private readonly string _destinationFilePath; private readonly CancellationToken? _cancellationToken; private HttpClient _httpClient; public delegate void ProgressChangedHandler(long? totalFileSize, long totalBytesDownloaded, double? progressPercentage); public event ProgressChangedHandler ProgressChanged; public HttpClientDownloadWithProgress(string downloadUrl, string destinationFilePath, CancellationToken? cancellationToken = null) { _downloadUrl = downloadUrl; _destinationFilePath = destinationFilePath; _cancellationToken = cancellationToken; } public async Task StartDownload() { _httpClient = new HttpClient { Timeout = TimeSpan.FromMinutes(10) }; using var response = await _httpClient.GetAsync(_downloadUrl, HttpCompletionOption.ResponseHeadersRead); await DownloadFileFromHttpResponseMessage(response); } private async Task DownloadFileFromHttpResponseMessage(HttpResponseMessage response) { response.EnsureSuccessStatusCode(); var totalBytes = response.Content.Headers.ContentLength; using var contentStream = await response.Content.ReadAsStreamAsync(); await ProcessContentStream(totalBytes, contentStream); } private async Task ProcessContentStream(long? totalDownloadSize, Stream contentStream) { var totalBytesRead = 0L; var readCount = 0L; var buffer = new byte[8192]; var isMoreToRead = true; using (var fileStream = new FileStream(_destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true)) { do { int bytesRead; if (_cancellationToken.HasValue) { bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, _cancellationToken.Value); } else { bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length); } if (bytesRead == 0) { isMoreToRead = false; continue; } await fileStream.WriteAsync(buffer, 0, bytesRead); totalBytesRead += bytesRead; readCount += 1; if (readCount % 10 == 0) TriggerProgressChanged(totalDownloadSize, totalBytesRead); } while (isMoreToRead); } //the last progress trigger should occur after the file handle has been released or you may get file locked error TriggerProgressChanged(totalDownloadSize, totalBytesRead); } private void TriggerProgressChanged(long? totalDownloadSize, long totalBytesRead) { if (ProgressChanged == null) return; double? progressPercentage = null; if (totalDownloadSize.HasValue) progressPercentage = Math.Round((double)totalBytesRead / totalDownloadSize.Value * 100); ProgressChanged(totalDownloadSize, totalBytesRead, progressPercentage); } public void Dispose() { _httpClient?.Dispose(); } } }