예제 #1
0
 private void OnGetFileInfo(DownloadFileInfo downloadFileInfo)
 {
     try
     {
         FileInfoReceived?.Invoke(this, new FileInfoReceivedEventArgs {
             DownloadFileInfo = downloadFileInfo
         });
     }
     catch (Exception ex)
     {
         OnExceptionThrown(ex);
     }
 }
예제 #2
0
        private List <Range> PrepareRanges(DownloadFileInfo info, int numberOfParallelDownloads)
        {
            var readRanges = new List <Range>();

            long lastRangeStart = 0;

            try
            {
                if (info.IsSupportedRange)
                {
                    for (int chunk = 0; chunk < numberOfParallelDownloads - 1; chunk++)
                    {
                        var range = new Range
                        {
                            Start = chunk * (info.Length / numberOfParallelDownloads),
                            End   = ((chunk + 1) * (info.Length / numberOfParallelDownloads)) - 1
                        };
                        readRanges.Add(range);
                        lastRangeStart = range.End + 1;
                    }
                }

                //last range which we add always even if the Range header is not supported
                readRanges.Add(new Range
                {
                    Start = lastRangeStart,
                    End   = info.Length - 1
                });

                for (int i = 0; i < readRanges.Count; i++)
                {
                    readRanges[i].Index  = i;
                    readRanges[i].Buffer = new byte[readRanges[i].End - readRanges[i].Start + 1];
                }
            }
            catch (Exception ex)
            {
                OnExceptionThrown(ex);
            }

            return(readRanges);
        }
예제 #3
0
        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
                        {
                            log.Debug($"{readRange.Index} started. Range: {readRange.Start}-{readRange.End}");

                            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;
                                    }

                                    log.Warn(
                                        $"Exception {ex.Message}. index={readRange.Index} offset={offset} uri={info.FileUrl}");
                                    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();
                            }

                            log.Debug($"{readRange.Index} completed. Success = {success}. Range: {readRange.Start}-{readRange.End}");
                        }
                        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);
        }
예제 #4
0
        public DownloadFileInfo GetFileInfo(string fileUrl)
        {
            var info = new DownloadFileInfo {
                FileUrl = fileUrl
            };

            int triesCount = 0;

            try
            {
                while (!info.IsOperationSuccess && triesCount < MaxTriesCount)
                {
                    try
                    {
                        //we must create web request each time to prevent DNS caching
                        var webRequest = (HttpWebRequest)WebRequest.Create(fileUrl);
                        webRequest.Method           = "HEAD";
                        webRequest.Timeout          = TimeoutMs;
                        webRequest.ReadWriteTimeout = TimeoutMs;

                        using (var webResponse = webRequest.GetResponse())
                        {
                            info.Length             = long.Parse(webResponse.Headers.Get("Content-Length"));
                            info.IsSupportedHead    = true;
                            info.Exists             = true;
                            info.IsOperationSuccess = true;
                        }
                    }
                    catch (WebException ex)
                    {
                        var status = (ex.Response as HttpWebResponse)?.StatusCode;

                        //File does not exist on server, return
                        if (status == HttpStatusCode.Forbidden || status == HttpStatusCode.NotFound)
                        {
                            return(info);
                        }

                        log.Warn("WebException during getting HEAD", ex);
                        WaitNetwork(ref triesCount);
                    }
                    catch (Exception ex)
                    {
                        log.Warn("Exception during getting HEAD download", ex);
                        WaitNetwork(ref triesCount);
                    }
                }

                info.IsOperationSuccess = false;
                triesCount = 0;
                while (!info.IsOperationSuccess && triesCount < MaxTriesCount)
                {
                    try
                    {
                        //we must create web request each time to prevent DNS caching
                        var webRequest = (HttpWebRequest)WebRequest.Create(fileUrl);
                        webRequest.AddRange(0, (int)info.Length - 1);
                        webRequest.Method           = "HEAD";
                        webRequest.Timeout          = TimeoutMs;
                        webRequest.ReadWriteTimeout = TimeoutMs;

                        using (var webResponse = (HttpWebResponse)webRequest.GetResponse())
                        {
                            info.IsSupportedRange = webResponse.StatusCode == HttpStatusCode.PartialContent ||
                                                    webResponse.GetResponseHeader("Accept-Ranges") == "bytes";

                            info.IsOperationSuccess = true;
                        }
                    }
                    catch (WebException ex)
                    {
                        var status = (ex.Response as HttpWebResponse)?.StatusCode;

                        //File does not exist on server, return
                        if (status == HttpStatusCode.Forbidden || status == HttpStatusCode.NotFound)
                        {
                            return(info);
                        }

                        log.Warn("WebException during getting HEAD", ex);
                        WaitNetwork(ref triesCount);
                    }
                    catch (Exception ex)
                    {
                        log.Warn("Exception during getting HEAD download", ex);
                        WaitNetwork(ref triesCount);
                    }
                }
            }
            catch (Exception ex)
            {
                OnExceptionThrown(ex);
            }

            return(info);
        }