namespace DiscordBotCore.Networking.Helpers; internal static class OnlineFunctions { /// /// Copy one Stream to another /// /// The base stream /// The destination stream /// The buffer to read /// The progress /// The cancellation token /// Triggered if any is empty /// Triggered if is less then or equal to 0 /// Triggered if is not readable /// Triggered in is not writable private static async Task CopyToOtherStreamAsync( this Stream stream, Stream destination, int bufferSize, IProgress? progress = null, CancellationToken cancellationToken = default) { if (stream == null) throw new ArgumentNullException(nameof(stream)); if (destination == null) throw new ArgumentNullException(nameof(destination)); if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize)); if (!stream.CanRead) throw new InvalidOperationException("The stream is not readable."); if (!destination.CanWrite) throw new ArgumentException("Destination stream is not writable", nameof(destination)); var buffer = new byte[bufferSize]; long totalBytesRead = 0; int bytesRead; while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken) .ConfigureAwait(false)) != 0) { await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); totalBytesRead += bytesRead; progress?.Report(totalBytesRead); } } /// /// Downloads a and saves it to another . /// /// The that is used to download the file /// The url to the file /// The to save the downloaded data /// The that is used to track the download progress /// The cancellation token /// internal static async Task DownloadFileAsync( this HttpClient client, string url, Stream destination, IProgress? progress = null, IProgress? downloadedBytes = null, int bufferSize = 81920, CancellationToken cancellation = default) { using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellation)) { var contentLength = response.Content.Headers.ContentLength; using (var download = await response.Content.ReadAsStreamAsync(cancellation)) { // Ignore progress reporting when no progress reporter was // passed or when the content length is unknown if (progress == null || !contentLength.HasValue) { await download.CopyToAsync(destination, cancellation); if (!contentLength.HasValue) progress?.Report(100f); return; } // Convert absolute progress (bytes downloaded) into relative progress (0% - 100%) // total ... 100% // downloaded ... x% // x = downloaded * 100 / total => x = downloaded / total * 100 var relativeProgress = new Progress(totalBytesDownloaded => { progress?.Report(totalBytesDownloaded / (float)contentLength.Value * 100); downloadedBytes?.Report(totalBytesDownloaded); } ); // Use extension method to report progress while downloading await download.CopyToOtherStreamAsync(destination, bufferSize, relativeProgress, cancellation); progress.Report(100f); } } } }