Ejemplo n.º 1
0
 /// <summary>
 /// Instantiates a new instance of the DownloadTask.DownloadProgress class.
 /// </summary>
 /// <param name="percent">Percent completed.</param>
 /// <param name="bytes">Number of bytes downloaded so far.</param>
 /// <param name="totalBytes">Total number of bytes to be downloaded, i.e. the file size.</param>
 /// <param name="speed">Download speed.</param>
 /// <param name="eta">Estimated time of arrival (completion).</param>
 public DownloadProgress(double percent, FileSize bytes, FileSize totalBytes, DownloadSpeed speed, TimeSpan eta, TimeSpan elapsed)
 {
     _percentComplete = percent;
     _bytesDownloaded = bytes;
     _totalBytes      = totalBytes;
     _speed           = speed;
     _eta             = eta;
     _elapsed         = elapsed;
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Starts downloading the file.
        /// </summary>
        /// <param name="saveLocation">Path to where the file will be saved.</param>
        /// <param name="onProgressChanged">Interface to track download progress.</param>
        /// <returns>A System.Threading.Task that represents the download operation.</returns>
        /// <exception cref="System.NotSupportedException">
        /// Invalid URI scheme.
        /// </exception>
        /// <exception cref="System.Security.SecurityException">
        /// Permission is not granted to connect to remote server.
        /// </exception>
        /// <exception cref="System.UriFormatException">The download URL is invalid.</exception>
        /// <exception cref="System.Net.WebException">An error occurred while processing the request.</exception>
        /// <exception cref="System.ArgumentException">
        /// path is an empty string (""), contains only white space, or contains one
        /// or more invalid characters.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error, such as specifying FileMode.CreateNew when the file specified
        /// by path already exists, occurred. -or-The stream has been closed.
        /// </exception>
        /// <exception cref="System.Security.SecurityException">
        /// The caller does not have the required permission.
        /// </exception>
        /// <exception cref="System.IO.DirectoryNotFoundException">
        /// The specified path is invalid, such as being on an unmapped drive.
        /// </exception>
        /// <exception cref="System.UnauthorizedAccessException">
        /// The access requested is not permitted by the operating system for the specified
        /// path, such as when access is Write or ReadWrite and the file or directory
        /// is set for read-only access.
        /// </exception>
        /// <exception cref="System.IO.PathTooLongException">
        /// The specified path, file name, or both exceed the system-defined maximum
        /// length. For example, on Windows-based platforms, paths must be less than
        /// 248 characters, and file names must be less than 260 characters.
        /// </exception>
        public Task DownloadAsync(string saveLocation, IProgress <DownloadProgress> onProgressChanged = null)
        {
            if (!IsInitialized)
            {
                throw new InvalidOperationException("The object has not been initialized. You must call DownloadTask.Initialize() first.");
            }

            if (isDisposed)
            {
                throw new ObjectDisposedException("DownloadTask", "Cannot start because this instance has been disposed.");
            }

            if (Status == DownloadTaskStatus.Failed || Status == DownloadTaskStatus.Canceled)
            {
                downloadWatch.Reset();
            }

            Status = DownloadTaskStatus.Starting;

            downloadWatch.Start();
            elapsedTimer.Start();

            FullPath = saveLocation; // filename after download
            FileName = Path.GetFileName(FullPath);

            _saveToIncomlete = FullPath + IncompleteDlExt;    // save here temporarily (until download completes)
            progressReporter = onProgressChanged;             // save so that it can be used for resumption
            cancelSource     = new CancellationTokenSource(); // disposed after paused, canceled, completed. a new one is use when resuming

            return(Task.Factory.StartNew(() => {
                HttpWebResponse response = null;
                FileStream fStream = null;

                try {
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_url);
                    request.UseDefaultCredentials = true;
                    request.Proxy = WebRequest.GetSystemWebProxy();

                    byte[] buffer = new byte[BufferSize];
                    FileMode mode;
                    long bytesDownloaded = 0;

                    if (File.Exists(_saveToIncomlete) && ResumeSupported)   // dl has been started before; try to resume
                    {
                        FileInfo fInfo = new FileInfo(_saveToIncomlete);

                        if (fInfo.Length < this.Size.Value)   // incomplete file must be smaller than total download size
                        // add range header to resume where left off
                        {
                            request.AddRange(fInfo.Length);
                            bytesDownloaded = fInfo.Length;
                            mode = FileMode.Append;
                        }
                        else   // otherwise, it's another file with the same name or it got currupted
                        {
                            File.Delete(_saveToIncomlete);
                            mode = FileMode.Create;
                        }
                    }
                    else   // resume not supported; start from beginning
                    {
                        mode = FileMode.Create;
                    }

                    // begin download
                    fStream = new FileStream(_saveToIncomlete, mode, FileAccess.Write);
                    response = (HttpWebResponse)request.GetResponse();

                    using (Stream dlStream = response.GetResponseStream()) { // download stream
                        // for measuring speed
                        const int reportInterval = 200;                      // ms
                        int tmpBytes = 0;

                        Stopwatch watch = new Stopwatch();
                        Func <DownloadProgress> ProgressSnapshot = () => {
                            double percent;
                            TimeSpan eta;
                            FileSize totalSize;
                            FileSize curSize = new FileSize(bytesDownloaded);
                            DownloadSpeed speed;

                            if (Status == DownloadTaskStatus.Completed)
                            {
                                speed = new DownloadSpeed(Size.Value, Elapsed);
                            }
                            else
                            {
                                speed = new DownloadSpeed(tmpBytes, watch.Elapsed); // current speed
                            }

                            if (Size.Value > 0)   // got file size from server
                            {
                                totalSize = new FileSize(this.Size.Value);
                                percent = (double)bytesDownloaded / Size.Value * 100;
                                eta = TimeSpan.FromSeconds((this.Size.Value - bytesDownloaded) / (bytesDownloaded / Elapsed.TotalSeconds)); // eta from average speed
                            }
                            else                                                                                                            // size is not known (happens with webpages)
                            {
                                totalSize = new FileSize(bytesDownloaded);                                                                  // use number of bytes downloaded so far as size
                                percent = -1;
                                eta = TimeSpan.FromSeconds(-1);
                            }

                            return new DownloadProgress(percent, curSize, totalSize, speed, eta, Elapsed);
                        };

                        while (true)
                        {
                            Status = DownloadTaskStatus.Downloading;

                            watch.Start();
                            int bytesRead = dlStream.Read(buffer, 0, buffer.Length);
                            watch.Stop();

                            if (watch.ElapsedMilliseconds >= reportInterval)
                            {
                                lock (Progress) {
                                    Progress.Update(ProgressSnapshot());
                                }
                                if (onProgressChanged != null)
                                {
                                    onProgressChanged.Report(Progress);
                                }

                                tmpBytes = 0;
                                watch.Reset();
                            }

                            bytesDownloaded += bytesRead;        // total downloaded so far
                            tmpBytes += bytesRead;               // total since last progress report

                            if (bytesRead < 1)                   // dl completed or failed
                            {
                                if (fStream.Length < Size.Value) // downloaded less than required
                                {
                                    Status = DownloadTaskStatus.Failed;
                                }
                                else
                                {
                                    Status = DownloadTaskStatus.Completed;
                                }
                                lock (Progress) {
                                    Progress.Update(ProgressSnapshot());
                                }
                                if (onProgressChanged != null)
                                {
                                    onProgressChanged.Report(Progress);
                                }

                                break;
                            }

                            // write block to file
                            fStream.Write(buffer, 0, bytesRead);
                            fStream.Flush();

                            try { // check for cancellation (pause or cancel)
                                cancelSource.Token.ThrowIfCancellationRequested();
                            }
                            catch (OperationCanceledException) {
                                if (Status == DownloadTaskStatus.Pausing)
                                {
                                    Status = DownloadTaskStatus.Paused;
                                }
                                else
                                {
                                    Status = DownloadTaskStatus.Canceled;
                                }

                                throw;
                            }
                        }
                    }
                }
                catch (Exception ex) {
                    if (!(ex is OperationCanceledException))   // something went wrong
                    {
                        Status = DownloadTaskStatus.Failed;

                        throw;
                    }
                }
                finally {
                    downloadWatch.Stop();
                    elapsedTimer.Stop();

                    if (response != null)
                    {
                        response.Dispose();
                    }

                    if (fStream != null)
                    {
                        fStream.Dispose();
                    }

                    if (cancelSource != null)
                    {
                        cancelSource.Dispose();
                        cancelSource = null;
                    }

                    if (Status == DownloadTaskStatus.Completed)
                    {
                        File.Move(_saveToIncomlete, _saveTo); // rename temporary file
                    }
                    else if (Status == DownloadTaskStatus.Canceled)
                    {
                        if (DeleteWhenCanceled)
                        {
                            File.Delete(_saveToIncomlete);
                        }
                    }
                }
            }, TaskCreationOptions.LongRunning));
        }