private async Task <IList <_ThreadProperty> > GetMultipleThreadProperties() { IList <_ThreadProperty> _out = new List <_ThreadProperty>(); _TotalSizeToDownload = TryGetContentLength(); long _SliceSize = _TotalSizeToDownload / _ThreadNumber; long _PrevStartSize = 0; long?_StartOffset = 0, _EndOffset = 0; for (int i = 0; i < (_ThreadSingleMode ? 1 : _ThreadNumber); i++) { HttpRequestMessage _RequestMessage = new HttpRequestMessage() { RequestUri = new Uri(_InputURL) }; _ThreadProperty ThreadProperty = new _ThreadProperty() { ThreadID = i, CurrentRetry = 1, LocalStream = SeekStreamToEnd(new FileStream(string.Format("{0}.{1:000}", _OutputPath, i + 1), FileMode.OpenOrCreate, FileAccess.Write)) }; _DownloadedSize += ThreadProperty.LocalStream.Length; ThreadProperty.StartOffset = _StartOffset = Math.Min(_PrevStartSize, _PrevStartSize += _SliceSize + 1); ThreadProperty.EndOffset = _EndOffset = i + 1 == _ThreadNumber ? _TotalSizeToDownload - 1 : ThreadProperty.StartOffset + _SliceSize; ThreadProperty.StartOffset += ThreadProperty.LocalStream.Length; if (ThreadProperty.EndOffset - ThreadProperty.StartOffset < -1) { Console.WriteLine($"Chunk on ThreadID: {i} seems to be corrupted (< expected size). Redownloading it!"); _DownloadedSize -= ThreadProperty.LocalStream.Length; ThreadProperty.StartOffset -= ThreadProperty.LocalStream.Length; ThreadProperty.LocalStream.Dispose(); ThreadProperty.LocalStream = new FileStream(string.Format("{0}.{1:000}", _OutputPath, i + 1), FileMode.Create, FileAccess.Write); } if (ThreadProperty.EndOffset - ThreadProperty.StartOffset > -1) { _RequestMessage.Headers.Range = new RangeHeaderValue(ThreadProperty.StartOffset, ThreadProperty.EndOffset); ThreadProperty.HttpMessage = CheckResponseStatusCode(await SendAsync(_RequestMessage, HttpCompletionOption.ResponseHeadersRead, _ThreadToken)); _out.Add(ThreadProperty); } else { ThreadProperty.LocalStream.Dispose(); } } return(_out); }
private async Task ReadStreamAsync(_ThreadProperty Property) { int read = 0; byte[] buffer = new byte[_DownloadBufferSize]; while ((read = await Property.RemoteStream.ReadAsync(buffer, 0, buffer.Length, _ThreadToken)) > 0) { _DownloadedSize += read; _LastContinuedSize += read; Property.LocalStream.Write(buffer, 0, read); UpdateProgress(new _DownloadProgress(_DownloadedSize, _TotalSizeToDownload, read, _LastContinuedSize, _Stopwatch.Elapsed, _DownloadState)); Property.CurrentRetry = 1; Property.IsDownloading = true; } }
private async Task <IList <_ThreadProperty> > GetSingleThreadProperties(long?StartOffset, long?EndOffset) { bool IsIgnore = false; long LocalLength = this._UseStreamOutput ? 0 : _OutputStream.Length; HttpRequestMessage RequestMessage = new HttpRequestMessage() { RequestUri = new Uri(_InputURL) }; _ThreadProperty ThreadProperty = new _ThreadProperty() { ThreadID = 0, CurrentRetry = 1, LocalStream = this._UseStreamOutput ? _OutputStream : SeekStreamToEnd(_OutputStream), StartOffset = this._UseStreamOutput ? StartOffset ?? 0 : (StartOffset == null ? LocalLength : LocalLength + StartOffset), EndOffset = EndOffset }; _DownloadedSize += LocalLength; if (ThreadProperty.EndOffset <= ThreadProperty.StartOffset) { ThreadProperty.StartOffset = 0; ThreadProperty.EndOffset = EndOffset; IsIgnore = true; } RequestMessage.Headers.Range = new RangeHeaderValue(ThreadProperty.StartOffset, ThreadProperty.EndOffset); ThreadProperty.HttpMessage = CheckResponseStatusCode(await SendAsync(RequestMessage, HttpCompletionOption.ResponseHeadersRead, _ThreadToken)); _TotalSizeToDownload += IsIgnore ? ThreadProperty.HttpMessage.Content.Headers.ContentLength ?? 0 : (ThreadProperty.HttpMessage.Content.Headers.ContentLength ?? 0) + LocalLength; if (IsIgnore) { return(new List <_ThreadProperty>()); } return(new List <_ThreadProperty> { ThreadProperty }); }
private async Task <bool> TryRunRetryableTask(_ThreadProperty ThreadProperty, bool IsLastRetry) { try { Console.WriteLine($"ThreadID: {ThreadProperty.ThreadID}, Start: {ThreadProperty.StartOffset}, EndOffset: {ThreadProperty.EndOffset}, Size: {ThreadProperty.EndOffset - ThreadProperty.StartOffset}"); await ReadStreamAsync(ThreadProperty); } catch (IOException ex) { Console.WriteLine($"I/O Error on ThreadID: {ThreadProperty.ThreadID}\r\n{ex}"); if (IsLastRetry) { ThreadProperty.LocalStream.Dispose(); ThreadProperty.IsDownloading = false; throw new IOException($"ThreadID: {ThreadProperty.ThreadID} has exceeded Max. Retry: {ThreadProperty.CurrentRetry - 1}/{_ThreadMaxRetry}. CANCELLING!!", ex); } return(false); } catch (HttpRequestException ex) { Console.WriteLine($"Error on ThreadID: {ThreadProperty.ThreadID}\r\n{ex}"); if (IsLastRetry) { ThreadProperty.LocalStream.Dispose(); ThreadProperty.IsDownloading = false; throw new HttpRequestException($"ThreadID: {ThreadProperty.ThreadID} has exceeded Max. Retry: {ThreadProperty.CurrentRetry - 1}/{_ThreadMaxRetry}. CANCELLING!!", ex); } return(false); } catch (ArgumentOutOfRangeException ex) { ThreadProperty.LocalStream.Dispose(); ThreadProperty.IsDownloading = false; Console.WriteLine($"Cancel: ThreadID: {ThreadProperty.ThreadID} has been shutdown!\r\nChunk of this thread is already completed. Ignoring!!\r\n{ex}"); return(true); } catch (InvalidDataException ex) { ThreadProperty.LocalStream.Dispose(); ThreadProperty.IsDownloading = false; Console.WriteLine($"Cancel: ThreadID: {ThreadProperty.ThreadID} has been shutdown!\r\n{ex}"); return(true); } catch (TaskCanceledException ex) { ThreadProperty.LocalStream.Dispose(); ThreadProperty.IsDownloading = false; Console.WriteLine($"Cancel: ThreadID: {ThreadProperty.ThreadID} has been shutdown!"); throw new TaskCanceledException(ex.ToString(), ex); } catch (OperationCanceledException ex) { ThreadProperty.LocalStream.Dispose(); ThreadProperty.IsDownloading = false; Console.WriteLine($"Cancel: ThreadID: {ThreadProperty.ThreadID} has been shutdown!"); throw new TaskCanceledException(ex.ToString(), ex); } catch (Exception ex) { ThreadProperty.LocalStream.Dispose(); ThreadProperty.IsDownloading = false; Console.WriteLine($"Unknown Error on ThreadID: {ThreadProperty.ThreadID}\r\n{ex}"); throw new Exception(ex.ToString(), ex); } return(true); }