Use HttpClient in FileDownloader class

This commit is contained in:
Jaex 2023-10-28 08:15:24 +03:00
parent 7085012fd4
commit bbb3d108f3
3 changed files with 100 additions and 118 deletions

View file

@ -26,21 +26,19 @@ You should have received a copy of the GNU General Public License
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Net; using System.Net.Http;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ShareX.HelpersLib namespace ShareX.HelpersLib
{ {
public class FileDownloader public class FileDownloader
{ {
public event EventHandler FileSizeReceived, DownloadStarted, ProgressChanged, DownloadCompleted, ExceptionThrown; public event Action FileSizeReceived, DownloadStarted, ProgressChanged, DownloadCompleted, ExceptionThrown;
public string URL { get; private set; } public string URL { get; private set; }
public string DownloadLocation { get; private set; } public string DownloadLocation { get; private set; }
public bool IsDownloading { get; private set; } public bool IsDownloading { get; private set; }
public bool IsCanceled { get; private set; } public bool IsCanceled { get; private set; }
public bool IsPaused { get; private set; }
public long FileSize { get; private set; } public long FileSize { get; private set; }
public long DownloadedSize { get; private set; } public long DownloadedSize { get; private set; }
public double DownloadSpeed { get; private set; } public double DownloadSpeed { get; private set; }
@ -58,30 +56,25 @@ public double DownloadPercentage
} }
} }
public IWebProxy Proxy { get; set; }
public string AcceptHeader { get; set; } public string AcceptHeader { get; set; }
public Exception LastException { get; private set; } public Exception LastException { get; private set; }
private const int bufferSize = 32768; private const int bufferSize = 32768;
public FileDownloader(string url, string downloadLocation, IWebProxy proxy = null, string acceptHeader = null) public FileDownloader(string url, string downloadLocation)
{ {
URL = url; URL = url;
DownloadLocation = downloadLocation; DownloadLocation = downloadLocation;
Proxy = proxy;
AcceptHeader = acceptHeader;
} }
public void StartDownload() public async Task StartDownload()
{ {
if (!IsDownloading && !string.IsNullOrEmpty(URL)) if (!IsDownloading && !string.IsNullOrEmpty(URL))
{ {
IsDownloading = true; IsDownloading = true;
IsCanceled = false; IsCanceled = false;
IsPaused = false;
Progress<EventHandler> progress = new Progress<EventHandler>(OnProgressChanged); await DoWork();
Task.Run(() => DoWork(progress));
} }
} }
@ -90,110 +83,88 @@ public void StopDownload()
IsCanceled = true; IsCanceled = true;
} }
public void PauseDownload() private async Task DoWork()
{
IsPaused = true;
}
public void ResumeDownload()
{
IsPaused = false;
}
private void OnProgressChanged(EventHandler eventHandler)
{
eventHandler?.Invoke(this, EventArgs.Empty);
}
private void DoWork(IProgress<EventHandler> progress)
{ {
try try
{ {
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL); HttpClient client = HttpClientFactory.Create();
request.UserAgent = ShareXResources.UserAgent;
request.Proxy = Proxy;
if (!string.IsNullOrEmpty(AcceptHeader)) using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, URL))
{ {
request.Accept = AcceptHeader; if (!string.IsNullOrEmpty(AcceptHeader))
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
FileSize = response.ContentLength;
progress.Report(FileSizeReceived);
if (FileSize > 0)
{ {
Stopwatch timer = new Stopwatch(); requestMessage.Headers.Accept.ParseAdd(AcceptHeader);
Stopwatch progressEventTimer = new Stopwatch(); }
long speedTest = 0;
byte[] buffer = new byte[(int)Math.Min(bufferSize, FileSize)]; using (HttpResponseMessage responseMessage = await client.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead))
int bytesRead; {
responseMessage.EnsureSuccessStatusCode();
using (FileStream fs = new FileStream(DownloadLocation, FileMode.Create, FileAccess.Write, FileShare.Read)) FileSize = responseMessage.Content.Headers.ContentLength ?? -1;
using (Stream responseStream = response.GetResponseStream())
FileSizeReceived?.Invoke();
if (FileSize > 0)
{ {
progress.Report(DownloadStarted); Stopwatch timer = new Stopwatch();
Stopwatch progressEventTimer = new Stopwatch();
long speedTest = 0;
while (DownloadedSize < FileSize && !IsCanceled) byte[] buffer = new byte[(int)Math.Min(bufferSize, FileSize)];
int bytesRead;
using (Stream responseStream = await responseMessage.Content.ReadAsStreamAsync())
using (FileStream fileStream = new FileStream(DownloadLocation, FileMode.Create, FileAccess.Write, FileShare.Read))
{ {
while (IsPaused && !IsCanceled) DownloadStarted?.Invoke();
while (DownloadedSize < FileSize && !IsCanceled)
{ {
timer.Reset(); if (!timer.IsRunning)
Thread.Sleep(10); {
} timer.Start();
}
if (IsCanceled) if (!progressEventTimer.IsRunning)
{ {
return; progressEventTimer.Start();
} }
if (!timer.IsRunning) bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
{ await fileStream.WriteAsync(buffer, 0, bytesRead);
timer.Start();
}
if (!progressEventTimer.IsRunning) DownloadedSize += bytesRead;
{ speedTest += bytesRead;
progressEventTimer.Start();
}
bytesRead = responseStream.Read(buffer, 0, buffer.Length); if (timer.ElapsedMilliseconds > 500)
fs.Write(buffer, 0, bytesRead); {
DownloadedSize += bytesRead; DownloadSpeed = (double)speedTest / timer.ElapsedMilliseconds * 1000;
speedTest += bytesRead; speedTest = 0;
timer.Reset();
}
if (timer.ElapsedMilliseconds > 500) if (progressEventTimer.ElapsedMilliseconds > 100)
{ {
DownloadSpeed = (double)speedTest / timer.ElapsedMilliseconds * 1000; ProgressChanged?.Invoke();
speedTest = 0;
timer.Reset();
}
if (progressEventTimer.ElapsedMilliseconds > 100) progressEventTimer.Reset();
{ }
progress.Report(ProgressChanged);
progressEventTimer.Reset();
} }
} }
}
progress.Report(ProgressChanged); ProgressChanged?.Invoke();
progress.Report(DownloadCompleted); DownloadCompleted?.Invoke();
}
} }
} }
} }
catch (Exception ex) catch (Exception e)
{ {
if (!IsCanceled) if (!IsCanceled)
{ {
LastException = ex; LastException = e;
progress.Report(ExceptionThrown); ExceptionThrown?.Invoke();
} }
} }
finally finally

View file

@ -594,10 +594,10 @@ public static async Task DownloadFileAsync(string url, string filePath)
{ {
if (responseMessage.IsSuccessStatusCode) if (responseMessage.IsSuccessStatusCode)
{ {
using (Stream stream = await responseMessage.Content.ReadAsStreamAsync()) using (Stream responseStream = await responseMessage.Content.ReadAsStreamAsync())
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{ {
await stream.CopyToAsync(fileStream); await responseStream.CopyToAsync(fileStream);
} }
} }
} }
@ -632,7 +632,7 @@ public static async Task<Bitmap> DownloadImageAsync(string url)
{ {
HttpClient client = HttpClientFactory.Create(); HttpClient client = HttpClientFactory.Create();
using (HttpResponseMessage responseMessage = await client.GetAsync(url)) using (HttpResponseMessage responseMessage = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
{ {
if (responseMessage.IsSuccessStatusCode && responseMessage.Content.Headers.ContentType != null) if (responseMessage.IsSuccessStatusCode && responseMessage.Content.Headers.ContentType != null)
{ {
@ -676,7 +676,8 @@ public static async Task<string> GetFileNameFromWebServerAsync(string url)
{ {
HttpClient client = HttpClientFactory.Create(); HttpClient client = HttpClientFactory.Create();
using (HttpResponseMessage responseMessage = await client.SendAsync(new HttpRequestMessage(HttpMethod.Head, url))) using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Head, url))
using (HttpResponseMessage responseMessage = await client.SendAsync(requestMessage))
{ {
if (responseMessage.Content.Headers.ContentDisposition != null) if (responseMessage.Content.Headers.ContentDisposition != null)
{ {

View file

@ -28,8 +28,8 @@ You should have received a copy of the GNU General Public License
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
namespace ShareX.HelpersLib namespace ShareX.HelpersLib
@ -42,7 +42,6 @@ public partial class DownloaderForm : Form
public string URL { get; set; } public string URL { get; set; }
public string FileName { get; set; } public string FileName { get; set; }
public string DownloadLocation { get; private set; } public string DownloadLocation { get; private set; }
public IWebProxy Proxy { get; set; }
public string AcceptHeader { get; set; } public string AcceptHeader { get; set; }
public bool AutoStartDownload { get; set; } public bool AutoStartDownload { get; set; }
public InstallType InstallType { get; set; } public InstallType InstallType { get; set; }
@ -57,7 +56,6 @@ private DownloaderForm()
InitializeComponent(); InitializeComponent();
ShareXResources.ApplyTheme(this); ShareXResources.ApplyTheme(this);
Proxy = HelpersOptions.CurrentProxy.GetWebProxy();
ChangeStatus(Resources.DownloaderForm_DownloaderForm_Waiting_); ChangeStatus(Resources.DownloaderForm_DownloaderForm_Waiting_);
Status = DownloaderFormStatus.Waiting; Status = DownloaderFormStatus.Waiting;
AutoStartDownload = true; AutoStartDownload = true;
@ -81,21 +79,21 @@ public DownloaderForm(UpdateChecker updateChecker) : this(updateChecker.Download
} }
} }
private void DownloaderForm_Shown(object sender, EventArgs e) private async void DownloaderForm_Shown(object sender, EventArgs e)
{ {
if (AutoStartDownload) if (AutoStartDownload)
{ {
StartDownload(); await StartDownload();
} }
} }
private void btnAction_MouseClick(object sender, MouseEventArgs e) private async void btnAction_MouseClick(object sender, MouseEventArgs e)
{ {
if (e.Button == MouseButtons.Left) if (e.Button == MouseButtons.Left)
{ {
if (Status == DownloaderFormStatus.Waiting) if (Status == DownloaderFormStatus.Waiting)
{ {
StartDownload(); await StartDownload();
} }
else if (Status == DownloaderFormStatus.DownloadCompleted) else if (Status == DownloaderFormStatus.DownloadCompleted)
{ {
@ -131,6 +129,7 @@ private void RunInstallerWithDelay(int delay = 1000)
Thread.Sleep(delay); Thread.Sleep(delay);
RunInstaller(); RunInstaller();
}); });
thread.Start(); thread.Start();
} }
else else
@ -197,17 +196,7 @@ private void ChangeStatus(string status)
lblStatus.Text = Helpers.SafeStringFormat(Resources.DownloaderForm_ChangeStatus_Status___0_, status); lblStatus.Text = Helpers.SafeStringFormat(Resources.DownloaderForm_ChangeStatus_Status___0_, status);
} }
private void ChangeProgress() private async Task StartDownload()
{
if (fileDownloader != null)
{
pbProgress.Value = (int)Math.Round(fileDownloader.DownloadPercentage);
lblProgress.Text = Helpers.SafeStringFormat(CultureInfo.CurrentCulture, Resources.DownloaderForm_ChangeProgress_Progress,
fileDownloader.DownloadPercentage, fileDownloader.DownloadSpeed / 1024, fileDownloader.DownloadedSize / 1024, fileDownloader.FileSize / 1024);
}
}
private void StartDownload()
{ {
if (!string.IsNullOrEmpty(URL) && Status == DownloaderFormStatus.Waiting) if (!string.IsNullOrEmpty(URL) && Status == DownloaderFormStatus.Waiting)
{ {
@ -220,19 +209,35 @@ private void StartDownload()
DebugHelper.WriteLine($"Downloading: \"{URL}\" -> \"{DownloadLocation}\""); DebugHelper.WriteLine($"Downloading: \"{URL}\" -> \"{DownloadLocation}\"");
fileDownloader = new FileDownloader(URL, DownloadLocation, Proxy, AcceptHeader); fileDownloader = new FileDownloader(URL, DownloadLocation);
fileDownloader.FileSizeReceived += (v1, v2) => ChangeProgress(); fileDownloader.AcceptHeader = AcceptHeader;
fileDownloader.DownloadStarted += (v1, v2) => ChangeStatus(Resources.DownloaderForm_StartDownload_Downloading_); fileDownloader.FileSizeReceived += FileDownloader_ProgressChanged;
fileDownloader.ProgressChanged += (v1, v2) => ChangeProgress(); fileDownloader.DownloadStarted += FileDownloader_DownloadStarted;
fileDownloader.DownloadCompleted += fileDownloader_DownloadCompleted; fileDownloader.ProgressChanged += FileDownloader_ProgressChanged;
fileDownloader.ExceptionThrown += (v1, v2) => ChangeStatus(fileDownloader.LastException.Message); fileDownloader.DownloadCompleted += FileDownloader_DownloadCompleted;
fileDownloader.StartDownload(); fileDownloader.ExceptionThrown += FileDownloader_ExceptionThrown;
await fileDownloader.StartDownload();
ChangeStatus(Resources.DownloaderForm_StartDownload_Getting_file_size_); ChangeStatus(Resources.DownloaderForm_StartDownload_Getting_file_size_);
} }
} }
private void fileDownloader_DownloadCompleted(object sender, EventArgs e) private void FileDownloader_DownloadStarted()
{
ChangeStatus(Resources.DownloaderForm_StartDownload_Downloading_);
}
private void FileDownloader_ProgressChanged()
{
if (fileDownloader != null)
{
pbProgress.Value = (int)Math.Round(fileDownloader.DownloadPercentage);
lblProgress.Text = Helpers.SafeStringFormat(CultureInfo.CurrentCulture, Resources.DownloaderForm_ChangeProgress_Progress,
fileDownloader.DownloadPercentage, fileDownloader.DownloadSpeed / 1024, fileDownloader.DownloadedSize / 1024, fileDownloader.FileSize / 1024);
}
}
private void FileDownloader_DownloadCompleted()
{ {
ChangeStatus(Resources.DownloaderForm_fileDownloader_DownloadCompleted_Download_completed_); ChangeStatus(Resources.DownloaderForm_fileDownloader_DownloadCompleted_Download_completed_);
Status = DownloaderFormStatus.DownloadCompleted; Status = DownloaderFormStatus.DownloadCompleted;
@ -244,6 +249,11 @@ private void fileDownloader_DownloadCompleted(object sender, EventArgs e)
} }
} }
private void FileDownloader_ExceptionThrown()
{
ChangeStatus(fileDownloader.LastException.Message);
}
private void DownloaderForm_FormClosing(object sender, FormClosingEventArgs e) private void DownloaderForm_FormClosing(object sender, FormClosingEventArgs e)
{ {
if (Status == DownloaderFormStatus.DownloadStarted && fileDownloader != null) if (Status == DownloaderFormStatus.DownloadStarted && fileDownloader != null)