using System; using System.IO; using System.Net; using System.Threading.Tasks; namespace RdtClient.Service.Services.Downloaders { public class SimpleDownloader : IDownloader { public event EventHandler DownloadComplete; public event EventHandler DownloadProgress; private readonly String _uri; private readonly String _filePath; private Int64 Speed { get; set; } private Int64 BytesTotal { get; set; } private Int64 BytesDone { get; set; } private Boolean _cancelled; private Int64 _bytesLastUpdate; private DateTime _nextUpdate; public SimpleDownloader(String uri, String filePath) { _uri = uri; _filePath = filePath; } public async Task Download() { Task.Run(async () => { await StartDownloadTask(); }); return null; } public Task Cancel() { _cancelled = true; return Task.CompletedTask; } private async Task StartDownloadTask() { try { _bytesLastUpdate = 0; _nextUpdate = DateTime.UtcNow.AddSeconds(1); // Determine the file size var webRequest = WebRequest.Create(_uri); webRequest.Method = "HEAD"; webRequest.Timeout = 5000; Int64 responseLength; using (var webResponse = await webRequest.GetResponseAsync()) { responseLength = Int64.Parse(webResponse.Headers.Get("Content-Length")); } var timeout = DateTimeOffset.UtcNow.AddHours(1); while (timeout > DateTimeOffset.UtcNow && !_cancelled) { try { var request = WebRequest.Create(_uri); using var response = await request.GetResponseAsync(); await using var stream = response.GetResponseStream(); if (stream == null) { throw new IOException("No stream"); } await using var fileStream = new FileStream(_filePath, FileMode.Create, FileAccess.Write, FileShare.Write); var buffer = new Byte[64 * 1024]; while (fileStream.Length < response.ContentLength && !_cancelled) { var read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length)); if (read > 0) { await fileStream.WriteAsync(buffer.AsMemory(0, read)); BytesDone = fileStream.Length; BytesTotal = responseLength; if (DateTime.UtcNow > _nextUpdate) { Speed = fileStream.Length - _bytesLastUpdate; _nextUpdate = DateTime.UtcNow.AddSeconds(1); _bytesLastUpdate = fileStream.Length; timeout = DateTimeOffset.UtcNow.AddHours(1); DownloadProgress?.Invoke(this, new DownloadProgressEventArgs { Speed = Speed, BytesDone = BytesDone, BytesTotal = BytesTotal }); } } else { break; } } break; } catch (IOException) { await Task.Delay(1000); } catch (WebException) { await Task.Delay(1000); } } if (timeout <= DateTimeOffset.UtcNow) { throw new Exception($"Download timed out"); } DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs()); } catch (Exception ex) { DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs { Error = ex.Message }); } } } }