You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
76 lines
2.8 KiB
76 lines
2.8 KiB
1 year ago
|
using System.Net.Http.Headers;
|
||
1 year ago
|
using Microsoft.Extensions.Logging;
|
||
1 year ago
|
using Polly.Contrib.WaitAndRetry;
|
||
1 year ago
|
using StabilityMatrix.Core.Models.Progress;
|
||
1 year ago
|
|
||
1 year ago
|
namespace StabilityMatrix.Core.Services;
|
||
1 year ago
|
|
||
|
public class DownloadService : IDownloadService
|
||
|
{
|
||
|
private readonly ILogger<DownloadService> logger;
|
||
1 year ago
|
private readonly IHttpClientFactory httpClientFactory;
|
||
1 year ago
|
private const int BufferSize = ushort.MaxValue;
|
||
1 year ago
|
|
||
1 year ago
|
public DownloadService(ILogger<DownloadService> logger, IHttpClientFactory httpClientFactory)
|
||
1 year ago
|
{
|
||
|
this.logger = logger;
|
||
1 year ago
|
this.httpClientFactory = httpClientFactory;
|
||
1 year ago
|
}
|
||
1 year ago
|
|
||
1 year ago
|
public async Task DownloadToFileAsync(string downloadUrl, string downloadPath,
|
||
1 year ago
|
IProgress<ProgressReport>? progress = null, string? httpClientName = null)
|
||
1 year ago
|
{
|
||
1 year ago
|
using var client = string.IsNullOrWhiteSpace(httpClientName)
|
||
|
? httpClientFactory.CreateClient()
|
||
|
: httpClientFactory.CreateClient(httpClientName);
|
||
|
|
||
|
client.Timeout = TimeSpan.FromMinutes(10);
|
||
1 year ago
|
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("StabilityMatrix", "1.0"));
|
||
1 year ago
|
await using var file = new FileStream(downloadPath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||
1 year ago
|
|
||
|
long contentLength = 0;
|
||
1 year ago
|
|
||
1 year ago
|
var response = await client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
||
|
contentLength = response.Content.Headers.ContentLength ?? 0;
|
||
|
|
||
1 year ago
|
var delays = Backoff.DecorrelatedJitterBackoffV2(
|
||
|
TimeSpan.FromMilliseconds(50), retryCount: 3);
|
||
|
|
||
|
foreach (var delay in delays)
|
||
1 year ago
|
{
|
||
1 year ago
|
if (contentLength > 0) break;
|
||
1 year ago
|
logger.LogDebug("Retrying get-headers for content-length");
|
||
1 year ago
|
await Task.Delay(delay);
|
||
1 year ago
|
response = await client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
||
|
contentLength = response.Content.Headers.ContentLength ?? 0;
|
||
|
}
|
||
|
var isIndeterminate = contentLength == 0;
|
||
|
|
||
|
await using var stream = await response.Content.ReadAsStreamAsync();
|
||
1 year ago
|
var totalBytesRead = 0L;
|
||
1 year ago
|
var buffer = new byte[BufferSize];
|
||
1 year ago
|
while (true)
|
||
|
{
|
||
|
var bytesRead = await stream.ReadAsync(buffer);
|
||
|
if (bytesRead == 0) break;
|
||
|
await file.WriteAsync(buffer.AsMemory(0, bytesRead));
|
||
|
|
||
|
totalBytesRead += bytesRead;
|
||
|
|
||
|
if (isIndeterminate)
|
||
|
{
|
||
1 year ago
|
progress?.Report(new ProgressReport(-1, isIndeterminate: true));
|
||
1 year ago
|
}
|
||
|
else
|
||
|
{
|
||
1 year ago
|
progress?.Report(new ProgressReport(current: Convert.ToUInt64(totalBytesRead),
|
||
1 year ago
|
total: Convert.ToUInt64(contentLength), message: "Downloading..."));
|
||
1 year ago
|
}
|
||
|
}
|
||
|
|
||
|
await file.FlushAsync();
|
||
1 year ago
|
|
||
|
progress?.Report(new ProgressReport(1f, message: "Download complete!"));
|
||
1 year ago
|
}
|
||
|
}
|