mirror of
https://github.com/4sval/FModel.git
synced 2026-04-10 11:07:21 -05:00
110 lines
3.8 KiB
C#
110 lines
3.8 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace FModel.Utils
|
|
{
|
|
/// <summary>
|
|
/// https://stackoverflow.com/questions/20661652/progress-bar-with-httpclient
|
|
/// </summary>
|
|
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();
|
|
}
|
|
}
|
|
}
|