Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Buffers;
- using System.IO;
- using System.Net.Http;
- using System.Threading;
- using System.Threading.Tasks;
- namespace Downloading_Test
- {
- public class DownloadingCore
- {
- public delegate void DownloadProgressHandler(long? totalFileSize, long totalBytesDownloaded, double? progressPercentage);
- public static class HttpClients
- {
- public static HttpClient General { get; } = new HttpClient();
- }
- private static readonly HttpClient _httpClient = new HttpClient();
- public static class DownloadWithProgress
- {
- public static async Task ExecuteAsync(HttpClient httpClient, string downloadPath, string destinationPath, DownloadProgressHandler progress, Func<HttpRequestMessage>? requestMessageBuilder = null, CancellationToken cancellationToken = default)
- {
- requestMessageBuilder ??= GetDefaultRequestBuilder(downloadPath);
- var download = new HttpClientDownloadWithProgress(DownloadingCore.HttpClients.General, destinationPath, requestMessageBuilder);
- // Use a unique identifier for each download to associate the correct event handler
- var downloadId = Guid.NewGuid();
- // Create a lambda function to capture the correct downloadId
- DownloadProgressHandler handler = (totalFileSize, totalBytesDownloaded, progressPercentage) =>
- {
- progress?.Invoke(totalFileSize, totalBytesDownloaded, progressPercentage);
- };
- download.ProgressChanged += handler;
- try
- {
- await download.StartDownload(cancellationToken);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Error during download: {ex.Message}");
- }
- finally
- {
- download.ProgressChanged -= handler;
- }
- }
- private static Func<HttpRequestMessage> GetDefaultRequestBuilder(string downloadPath)
- {
- return () => new HttpRequestMessage(HttpMethod.Get, downloadPath);
- }
- }
- internal class HttpClientDownloadWithProgress
- {
- private readonly HttpClient _httpClient;
- private readonly string _destinationFilePath;
- private readonly Func<HttpRequestMessage> _requestMessageBuilder;
- private int _bufferSize = 8192;
- public event DownloadProgressHandler? ProgressChanged;
- public HttpClientDownloadWithProgress(HttpClient httpClient, string destinationFilePath, Func<HttpRequestMessage> requestMessageBuilder)
- {
- _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
- _destinationFilePath = destinationFilePath ?? throw new ArgumentNullException(nameof(destinationFilePath));
- _requestMessageBuilder = requestMessageBuilder ?? throw new ArgumentNullException(nameof(requestMessageBuilder));
- }
- public async Task StartDownload(CancellationToken cancellationToken = default)
- {
- var requestMessage = _requestMessageBuilder.Invoke();
- long? totalFileSize = null; // Calculate or obtain the total file size
- if (totalFileSize.HasValue && totalFileSize <= 100 * 1024 * 1024)
- {
- if (requestMessage != null && requestMessage.RequestUri != null)
- {
- var streamingDownload = new StreamingDownloadWithProgress(_httpClient, _destinationFilePath);
- streamingDownload.ProgressChanged += (total, downloaded, progress) => ReportProgress(total, downloaded, progress);
- await streamingDownload.StartDownload(requestMessage.RequestUri, cancellationToken);
- }
- else
- {
- // Handle the case where requestMessage or its RequestUri is null
- // You might want to log a warning or throw an exception
- }
- }
- else
- {
- HttpResponseMessage response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
- await DownloadAsync(response, cancellationToken);
- }
- }
- private async Task DownloadAsync(HttpResponseMessage response, CancellationToken cancellationToken)
- {
- response.EnsureSuccessStatusCode();
- var totalBytes = response.Content.Headers.ContentLength;
- using (var contentStream = await response.Content.ReadAsStreamAsync())
- {
- await ProcessContentStream(totalBytes, contentStream, cancellationToken);
- }
- }
- private async Task ProcessContentStream(long? totalDownloadSize, Stream contentStream, CancellationToken cancellationToken)
- {
- var totalBytesRead = 0L;
- var readCount = 0L;
- var buffer = ArrayPool<byte>.Shared.Rent(_bufferSize);
- var isMoreToRead = true;
- try
- {
- using (var fileStream = new FileStream(_destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, _bufferSize, true))
- {
- do
- {
- cancellationToken.ThrowIfCancellationRequested();
- var bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
- if (bytesRead == 0)
- {
- isMoreToRead = false;
- ReportProgress(totalDownloadSize, totalBytesRead, null); // Pass null for progress as it's not available in this context
- continue;
- }
- await fileStream.WriteAsync(buffer, 0, bytesRead, cancellationToken);
- totalBytesRead += bytesRead;
- readCount += 1;
- if (readCount % 100 == 0)
- ReportProgress(totalDownloadSize, totalBytesRead, null);
- } while (isMoreToRead);
- }
- }
- finally
- {
- ArrayPool<byte>.Shared.Return(buffer);
- }
- }
- private void ReportProgress(long? totalDownloadSize, long totalBytesRead, double? progress)
- {
- double? progressPercentage = null;
- if (totalDownloadSize.HasValue && totalDownloadSize.Value > 0)
- {
- progressPercentage = progress ?? Math.Round((double)totalBytesRead / totalDownloadSize.Value * 100, 2);
- }
- ProgressChanged?.Invoke(totalDownloadSize, totalBytesRead, progressPercentage);
- }
- }
- // Add the new StreamingDownloadWithProgress class
- internal class StreamingDownloadWithProgress
- {
- private readonly HttpClient _httpClient;
- private readonly string _destinationFilePath;
- private int _bufferSize = 8192;
- public event DownloadProgressHandler? ProgressChanged;
- public StreamingDownloadWithProgress(HttpClient httpClient, string destinationFilePath)
- {
- _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
- _destinationFilePath = destinationFilePath ?? throw new ArgumentNullException(nameof(destinationFilePath));
- }
- public async Task StartDownload(Uri downloadUri, CancellationToken cancellationToken = default)
- {
- using var response = await _httpClient.GetAsync(downloadUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
- response.EnsureSuccessStatusCode();
- var totalBytes = response.Content.Headers.ContentLength;
- using var contentStream = await response.Content.ReadAsStreamAsync();
- await ProcessContentStream(totalBytes, contentStream, cancellationToken);
- }
- private async Task ProcessContentStream(long? totalDownloadSize, Stream contentStream, CancellationToken cancellationToken)
- {
- var totalBytesRead = 0L;
- var readCount = 0L;
- var buffer = ArrayPool<byte>.Shared.Rent(_bufferSize);
- var isMoreToRead = true;
- try
- {
- using var fileStream = new FileStream(_destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, _bufferSize, true);
- do
- {
- cancellationToken.ThrowIfCancellationRequested();
- var bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
- if (bytesRead == 0)
- {
- isMoreToRead = false;
- ReportProgress(totalDownloadSize, totalBytesRead);
- continue;
- }
- await fileStream.WriteAsync(buffer, 0, bytesRead, cancellationToken);
- totalBytesRead += bytesRead;
- readCount += 1;
- if (readCount % 100 == 0)
- ReportProgress(totalDownloadSize, totalBytesRead);
- } while (isMoreToRead);
- }
- finally
- {
- ArrayPool<byte>.Shared.Return(buffer);
- }
- }
- private void ReportProgress(long? totalDownloadSize, long totalBytesRead)
- {
- double? progressPercentage = null;
- if (totalDownloadSize.HasValue && totalDownloadSize.Value > 0)
- {
- progressPercentage = Math.Round((double)totalBytesRead / totalDownloadSize.Value * 100, 2);
- }
- ProgressChanged?.Invoke(totalDownloadSize, totalBytesRead, progressPercentage);
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment