public DownloadResult Download(string fileUrl, string filePath, int numberOfParallelDownloads, CancellationToken cancellationToken) { try { var uri = new Uri(fileUrl); var downloadFileInfo = GetFileInfo(fileUrl); OnGetFileInfo(downloadFileInfo); DownloadResult result; if (!downloadFileInfo.Exists || !downloadFileInfo.IsOperationSuccess) { result = new DownloadResult { FileUrl = fileUrl, FilePath = filePath, FileExists = downloadFileInfo.Exists, IsOperationSuccess = downloadFileInfo.IsOperationSuccess, IsCancelled = cancellationToken.IsCancellationRequested }; OnDownloadComplete(result); return(result); } //Handle number of parallel downloads if (numberOfParallelDownloads <= 0) { numberOfParallelDownloads = Environment.ProcessorCount; } var readRanges = PrepareRanges(downloadFileInfo, numberOfParallelDownloads); var sw = Stopwatch.StartNew(); result = DownloadRanges(downloadFileInfo, readRanges, cancellationToken); sw.Stop(); result.TimeTakenMs = sw.ElapsedMilliseconds; result.FilePath = filePath; if (!result.IsCancelled) { result.IsOperationSuccess &= WriteFile(filePath, readRanges); } OnDownloadComplete(result); return(result); } catch (Exception ex) { OnExceptionThrown(ex); } return(new DownloadResult { FileUrl = fileUrl, FilePath = filePath, IsOperationSuccess = false }); }
public void OnDownloadComplete(DownloadResult result) { try { DownloadCompleted?.Invoke(this, new DownloadCompletedEventArgs { Result = result }); } catch (Exception ex) { OnExceptionThrown(ex); } }
private DownloadResult DownloadRanges(DownloadFileInfo info, List <Range> readRanges, CancellationToken cancel) { // Parallel download var result = new DownloadResult { FileUrl = info.FileUrl, FileExists = info.Exists, FileLength = info.Length, ParallelDownloads = readRanges.Count }; long bytesDownloaded = 0; try { if (cancel.IsCancellationRequested) { result.IsCancelled = true; return(result); } int numberOfThreads = readRanges.Count; var mutex = new ManualResetEvent(false); var progressChangedEventArgs = new ProgressChangedEventArgs { FileUrl = info.FileUrl, FileLength = info.Length }; StartProgressThread(progressChangedEventArgs); StartBandwidthThread(progressChangedEventArgs); var sw = Stopwatch.StartNew(); foreach (var readRange in readRanges) { new Thread(_ => { try { int rangeLen = readRange.Buffer.Length; int offset = 0; const int blockSize = 4096; int triesCount = 0; bool success = false; while (!success && !mutex.WaitOne(0) && triesCount < MaxTriesCount) { try { var httpWebRequest = (HttpWebRequest)WebRequest.Create(info.FileUrl); httpWebRequest.Method = "GET"; if (info.IsSupportedRange) { httpWebRequest.AddRange((int)readRange.Start + offset, (int)readRange.End); } httpWebRequest.Timeout = TimeoutMs; httpWebRequest.ReadWriteTimeout = TimeoutMs; using (var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse()) { using (var responseStream = httpWebResponse.GetResponseStream()) { int bytesRead; while ((bytesRead = responseStream.Read(readRange.Buffer, offset, rangeLen - offset < blockSize ? rangeLen - offset : blockSize )) > 0 && !cancel.IsCancellationRequested) { offset += bytesRead; Interlocked.Add(ref bytesDownloaded, bytesRead); lock (progressChangedEventArgs) { progressChangedEventArgs.Progress = bytesDownloaded; progressChangedEventArgs.ElapsedMs = sw.ElapsedMilliseconds; Monitor.PulseAll(progressChangedEventArgs); } } } success = true; } } catch (Exception ex) { //reset offset if server does not support range if (!info.IsSupportedRange) { offset = 0; } WaitNetwork(ref triesCount); } } //If one thread is failed signalize other threads to exit. //If all threads completed signalize the method to return download result if (!success || Interlocked.Decrement(ref numberOfThreads) == 0) { mutex.Set(); } } catch (Exception ex) { OnExceptionThrown(ex); } }) { IsBackground = true }.Start(); } mutex.WaitOne(); sw.Stop(); result.BytesDownloaded = bytesDownloaded; result.IsOperationSuccess = Interlocked.CompareExchange(ref numberOfThreads, 0, 0) == 0; result.IsCancelled = cancel.IsCancellationRequested; } catch (Exception ex) { OnExceptionThrown(ex); } return(result); }