Exemple #1
0
        /// <summary>
        /// Main entry point
        /// </summary>
        /// <param name="args">Arguments to be passed to the application</param>
        public static void Main(string[] args)
        {
            try
            {
                Downloader.ProgressUpdateEvent += Print;
                string retry;
                do
                {
                    Print("How many parallel downloads do you want to execute?");
                    int            numOfParallelDownloads = int.Parse(Console.ReadLine());
                    DownloadResult result = Downloader.Download(ConfigurationManager.AppSettings["fileUrl"], ConfigurationManager.AppSettings["downloadLocation"], numOfParallelDownloads);

                    Print($"Download Summary:\n FileSize: {DisplayFormatHelper.FormatSize(result.Size)}\n Number of chunks: {numOfParallelDownloads}" +
                          $"\n Chunk size: {DisplayFormatHelper.FormatSize(result.ChunkSize)}\n Time taken : {DisplayFormatHelper.TimeSpanDisplayFormat(result.TimeTaken)} \n Downloaded File: {result.FilePath}");

                    Print("Try again? (Y/N)");
                    retry = Console.ReadLine();
                }while (!string.IsNullOrWhiteSpace(retry) && retry.ToLower() == "y");
            }
            catch (FriendlyException ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.Read();
        }
Exemple #2
0
        private static void UpdateProgress(long chunkSize, TimeSpan chunkTime, TimeSpan totalTime, long total, int parallelDownloads)
        {
            if (ProgressUpdateEvent == null)
            {
                return;
            }
            StringBuilder sb = new StringBuilder();

            //Given the size of the chunk just downloaded and the chunks remaining, calculate the time remaining for the download to complete.
            sb.Append($"{downloadedChunks * 100 / total}% - Speed {DisplayFormatHelper.FormatSize((long)(chunkSize / chunkTime.TotalSeconds))}ps");
            if (total > downloadedChunks)
            {
                //Assuming all the downloads start at relatively the same time ((total - downloadedChunks) * chunkTime.Ticks) - chunkTime.Ticks))
                sb.Append($" - Estimated time remaining {DisplayFormatHelper.TimeSpanDisplayFormat((long)((total - downloadedChunks) * chunkTime.Ticks) - chunkTime.Ticks)}");
            }
            ProgressUpdateEvent(sb.ToString());
        }
Exemple #3
0
        public static DownloadResult Download(string fileUrl, string destinationFolderPath, int numberOfParallelDownloads)
        {
            downloadedChunks = 0;
            Uri uri = new Uri(fileUrl);

            //Input validation
            if (!Directory.Exists(destinationFolderPath))
            {
                throw new FriendlyException($"Invalid value for destinationFolderPath. Directory {destinationFolderPath} does not exist.");
            }
            if (numberOfParallelDownloads <= 0)
            {
                throw new FriendlyException("Invalid value for numberOfParallelDownloads. Please enter a value greater than zero.");
            }
            //Calculate destination path
            String destinationFilePath = Path.Combine(destinationFolderPath, uri.Segments.Last());

            DownloadResult result = new DownloadResult()
            {
                FilePath = destinationFilePath
            };

            #region Get file size
            WebRequest webRequest = HttpWebRequest.Create(fileUrl);
            webRequest.Method = "HEAD";
            long responseLength;
            using (WebResponse webResponse = webRequest.GetResponse())
            {
                if (!webResponse.Headers.AllKeys.Contains("Content-Length"))
                {
                    throw new FriendlyException("Unable to download file. Content-Length not present.");
                }
                responseLength = long.Parse(webResponse.Headers.Get("Content-Length"));
                result.Size    = responseLength;
            }
            #endregion
            UpdateProgress($"File Size:{responseLength} bytes");
            if (File.Exists(destinationFilePath))
            {
                File.Delete(destinationFilePath);
            }
            if (responseLength < numberOfParallelDownloads)
            {
                throw new FriendlyException($"The file is too small to be divided into chunks to have {numberOfParallelDownloads} parallel downloads. Please select a value less than {numberOfParallelDownloads}.");
            }
            UpdateProgress("Dividing in to chunks...");

            ConcurrentDictionary <long, string> tempFilesDictionary = new ConcurrentDictionary <long, string>();

            #region Calculate ranges
            List <DataRange> readRanges = new List <DataRange>();
            for (int chunk = 0; chunk < numberOfParallelDownloads - 1; chunk++)
            {
                DataRange range = new DataRange()
                {
                    Start = chunk * (responseLength / numberOfParallelDownloads),
                    End   = ((chunk + 1) * (responseLength / numberOfParallelDownloads)) - 1
                };
                readRanges.Add(range);
            }

            readRanges.Add(new DataRange()
            {
                Start = readRanges.Any() ? readRanges.Last().End + 1 : 0,
                End   = responseLength - 1
            });
            result.ChunkSize = readRanges[0].End - readRanges[0].Start;
            #endregion
            UpdateProgress($"Divided into {numberOfParallelDownloads} chunks of {readRanges[0].End - readRanges[0].Start} bytes each.");
            DateTime startTime = DateTime.Now;

            #region Parallel download

            long total = readRanges.Count();
            UpdateProgress("Starting downloads...");
            Parallel.ForEach(readRanges, new ParallelOptions()
            {
                MaxDegreeOfParallelism = numberOfParallelDownloads
            }, readRange =>
            {
                DateTime chunkStart           = DateTime.Now;
                HttpWebRequest httpWebRequest = HttpWebRequest.Create(fileUrl) as HttpWebRequest;
                httpWebRequest.Method         = "GET";
                httpWebRequest.AddRange(readRange.Start, readRange.End);
                using (HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse)
                {
                    String tempFilePath = Path.GetTempFileName();
                    using (FileStream fileStream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write, FileShare.Write))
                    {
                        httpWebResponse.GetResponseStream().CopyTo(fileStream);
                        tempFilesDictionary.TryAdd(readRange.Start, tempFilePath);
                    }
                }

                downloadedChunks++;
                UpdateProgress(readRange.End - readRange.Start, DateTime.Now - chunkStart, DateTime.Now - startTime, total, numberOfParallelDownloads);
            });


            #endregion

            result.TimeTaken = DateTime.Now.Subtract(startTime);
            UpdateProgress($"Total time for downloading : {DisplayFormatHelper.TimeSpanDisplayFormat(result.TimeTaken)}");
            UpdateProgress("Merging chunks..");
            #region Merge to single file
            using (FileStream destinationStream = new FileStream(destinationFilePath, FileMode.Append))
            {
                foreach (KeyValuePair <long, string> tempFile in tempFilesDictionary.OrderBy(b => b.Key))
                {
                    byte[] tempFileBytes = File.ReadAllBytes(tempFile.Value);
                    destinationStream.Write(tempFileBytes, 0, tempFileBytes.Length);
                    File.Delete(tempFile.Value);
                }
                #endregion
            }
            result.TimeTaken = DateTime.Now.Subtract(startTime);
            UpdateProgress("Process complete!");
            return(result);
        }