mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-14 03:16:40 +10:00
Download: Improve download reliability
This commit is contained in:
@@ -27,7 +27,9 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Data;
|
using System.Windows.Data;
|
||||||
|
|
||||||
namespace WPinternals
|
namespace WPinternals
|
||||||
@@ -498,7 +500,7 @@ namespace WPinternals
|
|||||||
internal string URL;
|
internal string URL;
|
||||||
internal string[] URLCollection;
|
internal string[] URLCollection;
|
||||||
internal string Folder;
|
internal string Folder;
|
||||||
internal MyWebClient Client;
|
internal HttpClient Client;
|
||||||
internal long SpeedIndex = -1;
|
internal long SpeedIndex = -1;
|
||||||
internal long[] Speeds = new long[10];
|
internal long[] Speeds = new long[10];
|
||||||
internal long LastBytesReceived;
|
internal long LastBytesReceived;
|
||||||
@@ -520,18 +522,16 @@ namespace WPinternals
|
|||||||
{
|
{
|
||||||
Size = DownloadsViewModel.GetFileLengthFromURL(URL);
|
Size = DownloadsViewModel.GetFileLengthFromURL(URL);
|
||||||
|
|
||||||
Client = new MyWebClient();
|
Client = new HttpClient();
|
||||||
Client.DownloadFileCompleted += Client_DownloadFileCompleted;
|
_ = Client.DownloadFileAsync(Uri, Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(Uri.LocalPath)), Client_DownloadProgressChanged, Client_DownloadFileCompleted);
|
||||||
Client.DownloadProgressChanged += Client_DownloadProgressChanged;
|
|
||||||
Client.DownloadFileAsync(Uri, Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(Uri.LocalPath)), null);
|
|
||||||
}).Start();
|
}).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
|
private void Client_DownloadFileCompleted(bool Error)
|
||||||
{
|
{
|
||||||
void Finish()
|
void Finish()
|
||||||
{
|
{
|
||||||
Status = e.Error == null ? DownloadStatus.Ready : DownloadStatus.Failed;
|
Status = Error ? DownloadStatus.Failed : DownloadStatus.Ready;
|
||||||
App.DownloadManager.DownloadList.Remove(this);
|
App.DownloadManager.DownloadList.Remove(this);
|
||||||
if (Status == DownloadStatus.Ready)
|
if (Status == DownloadStatus.Ready)
|
||||||
{
|
{
|
||||||
@@ -560,7 +560,7 @@ namespace WPinternals
|
|||||||
UIContext?.Post(d => Finish(), null);
|
UIContext?.Post(d => Finish(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
|
private void Client_DownloadProgressChanged(HttpClientDownloadProgress e)
|
||||||
{
|
{
|
||||||
BytesReceived = e.BytesReceived;
|
BytesReceived = e.BytesReceived;
|
||||||
Progress = e.ProgressPercentage;
|
Progress = e.ProgressPercentage;
|
||||||
@@ -796,13 +796,94 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class MyWebClient : WebClient
|
public class HttpClientDownloadProgress
|
||||||
{
|
{
|
||||||
protected override WebRequest GetWebRequest(Uri address)
|
//
|
||||||
|
// Summary:
|
||||||
|
// Gets the asynchronous task progress percentage.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// A percentage value indicating the asynchronous task progress.
|
||||||
|
public int ProgressPercentage { get; }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Summary:
|
||||||
|
// Gets the number of bytes received.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// An System.Int64 value that indicates the number of bytes received.
|
||||||
|
public long BytesReceived { get; }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Summary:
|
||||||
|
// Gets the total number of bytes in a System.Net.WebClient data download operation.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// An System.Int64 value that indicates the number of bytes that will be received.
|
||||||
|
public long TotalBytesToReceive { get; }
|
||||||
|
|
||||||
|
internal HttpClientDownloadProgress(long BytesReceived, long TotalBytesToReceive)
|
||||||
{
|
{
|
||||||
HttpWebRequest req = (HttpWebRequest)base.GetWebRequest(address);
|
this.TotalBytesToReceive = TotalBytesToReceive;
|
||||||
req.ServicePoint.ConnectionLimit = 10;
|
this.BytesReceived = BytesReceived;
|
||||||
return req;
|
ProgressPercentage = (int)Math.Round((float)BytesReceived / TotalBytesToReceive * 100f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HttpClientProgressExtensions
|
||||||
|
{
|
||||||
|
public static async Task DownloadFileAsync(this HttpClient client, Uri address, string fileName, Action<HttpClientDownloadProgress> progress = null, Action<bool> completed = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using FileStream destination = File.Create(fileName);
|
||||||
|
using HttpResponseMessage response = await client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
long? contentLength = response.Content.Headers.ContentLength;
|
||||||
|
using Stream download = await response.Content.ReadAsStreamAsync();
|
||||||
|
|
||||||
|
if (progress is null || !contentLength.HasValue)
|
||||||
|
{
|
||||||
|
await download.CopyToAsync(destination);
|
||||||
|
if (completed != null)
|
||||||
|
completed(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Progress<long> progressWrapper = new Progress<long>(totalBytes => progress(new HttpClientDownloadProgress(totalBytes, contentLength.Value)));
|
||||||
|
await download.CopyToAsync(destination, 81920, progressWrapper, cancellationToken);
|
||||||
|
|
||||||
|
if (completed != null)
|
||||||
|
completed(true);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (completed != null)
|
||||||
|
completed(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long> progress = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
if (bufferSize < 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(bufferSize));
|
||||||
|
if (source is null)
|
||||||
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
if (!source.CanRead)
|
||||||
|
throw new InvalidOperationException($"'{nameof(source)}' is not readable.");
|
||||||
|
if (destination == null)
|
||||||
|
throw new ArgumentNullException(nameof(destination));
|
||||||
|
if (!destination.CanWrite)
|
||||||
|
throw new InvalidOperationException($"'{nameof(destination)}' is not writable.");
|
||||||
|
|
||||||
|
byte[] buffer = new byte[bufferSize];
|
||||||
|
long totalBytesRead = 0;
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
|
||||||
|
{
|
||||||
|
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
|
progress?.Report(totalBytesRead);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user