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);
}
}
}
}