扩展自定义可恢复下载类

时间:2015-07-22 15:11:36

标签: c#

我的Download类中有一个名为DownloadProgressChanged的事件处理程序。它通过DownloadProgressChangedEventArgs类接受四个参数,它们是BytesReceived,TotalBytesToReceive,ProgressPercentage& CurrentSpeed。

我可以下载和恢复文件。也阻止他们。现在添加由DownloadProgressEventArgs提供的数据让我感到困惑。主要问题是我不知道BytesReceived参数所需的信息在哪里。有人能指出我正确的方向吗?

prim_file:list_dir(Port, Directory)

而且byteSize根本不会改变。由于某种原因,它始终保持不变。

1 个答案:

答案 0 :(得分:0)

这是一个非常棒的无bug工作可恢复下载类,有很多改进:

public class Download
{
    public event EventHandler<DownloadStatusChangedEventArgs> DownloadStatusChanged;
    public event EventHandler<DownloadProgressChangedEventArgs> DownloadProgressChanged;
    public event EventHandler DownloadCompleted;

    public bool stop = true; // by default stop is true

    public void DownloadFile(string DownloadLink, string Path)
    {
        stop = false; // always set this bool to false, everytime this method is called

        long ExistingLength = 0;
        FileStream saveFileStream;

        if (File.Exists(Path))
        {
            FileInfo fileInfo = new FileInfo(Path);
            ExistingLength = fileInfo.Length;
        }

        if (ExistingLength > 0)
            saveFileStream = new FileStream(Path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
        else
            saveFileStream = new FileStream(Path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);

        var request = (HttpWebRequest)HttpWebRequest.Create(DownloadLink);
        request.Proxy = null;
        request.AddRange(ExistingLength);

        try
        {
            using (var response = (HttpWebResponse)request.GetResponse())
            {
                long FileSize = ExistingLength + response.ContentLength; //response.ContentLength gives me the size that is remaining to be downloaded
                bool downloadResumable; // need it for sending empty progress

                if ((int)response.StatusCode == 206)
                {
                    Console.WriteLine("Resumable");
                    var downloadStatusArgs = new DownloadStatusChangedEventArgs();
                    downloadResumable = true;
                    downloadStatusArgs.ResumeSupported = downloadResumable;
                    OnDownloadStatusChanged(downloadStatusArgs);
                }
                else // sometimes a server that supports partial content will lose its ability to send partial content(weird behavior) and thus the download will lose its resumability
                {
                    Console.WriteLine("Resume Not Supported");
                    ExistingLength = 0;
                    var downloadStatusArgs = new DownloadStatusChangedEventArgs();
                    downloadResumable = false;
                    downloadStatusArgs.ResumeSupported = downloadResumable;
                    OnDownloadStatusChanged(downloadStatusArgs);
                    // restart downloading the file from the beginning because it isn't resumable
                    // if this isn't done, the method downloads the file from the beginning and starts writing it after the previously half downloaded file, thus increasing the filesize and corrupting the downloaded file
                    saveFileStream.Dispose(); // dispose object to free it for the next operation
                    File.WriteAllText(Path, string.Empty); // clear the contents of the half downloaded file that can't be resumed
                    saveFileStream = saveFileStream = new FileStream(Path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite); // reopen it for writing
                }

                using (var stream = response.GetResponseStream())
                {
                    byte[] downBuffer = new byte[4096];
                    int byteSize = 0;
                    long totalReceived = byteSize + ExistingLength;
                    var sw = new Stopwatch();
                    sw.Start();
                    while ((byteSize = stream.Read(downBuffer, 0, downBuffer.Length)) > 0)
                    {
                        saveFileStream.Write(downBuffer, 0, byteSize);
                        totalReceived += byteSize;

                        var args = new DownloadProgressChangedEventArgs();
                        args.BytesReceived = totalReceived;
                        args.TotalBytesToReceive = FileSize;
                        float currentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds;
                        args.CurrentSpeed = currentSpeed;
                        if (downloadResumable == true)
                        {
                            args.ProgressPercentage = ((float)totalReceived / (float)FileSize) * 100;
                            long bytesRemainingtoBeReceived = FileSize - totalReceived;
                            args.TimeLeft = (long)(bytesRemainingtoBeReceived / currentSpeed);
                        }
                        else
                        {
                            //args.ProgressPercentage = Unknown;
                            //args.TimeLeft = Unknown;
                        }
                        OnDownloadProgressChanged(args);

                        if (stop == true) 
                            return;
                    }
                    sw.Stop();
                }
            }
            var completedArgs = new EventArgs();
            OnDownloadCompleted(completedArgs);
            saveFileStream.Dispose();
        }
        catch (WebException e)
        {
            string filename = System.IO.Path.GetFileName(Path);
            Console.WriteLine(e.Message);
            saveFileStream.Dispose();
            return; //not needed because this is the last line of the method, but let's keep it here
        }
    }

    public void StopDownload()
    {
        stop = true;
    }

    protected virtual void OnDownloadStatusChanged(DownloadStatusChangedEventArgs e)
    {
        EventHandler<DownloadStatusChangedEventArgs> handler = DownloadStatusChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }

    protected virtual void OnDownloadProgressChanged(DownloadProgressChangedEventArgs e)
    {
        EventHandler<DownloadProgressChangedEventArgs> handler = DownloadProgressChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }

    protected virtual void OnDownloadCompleted(EventArgs e)
    {
        EventHandler handler = DownloadCompleted;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

public class DownloadStatusChangedEventArgs : EventArgs
{
    public bool ResumeSupported { get; set; }
}

public class DownloadProgressChangedEventArgs : EventArgs
{
    public long BytesReceived { get; set; }
    public long TotalBytesToReceive { get; set; }
    public float ProgressPercentage { get; set; }
    public float CurrentSpeed { get; set; } // in bytes
    public long TimeLeft { get; set; } // in seconds
}

CurrentSpeed计算不够精确,因为它需要时间才能准确。更好的方法是找出应用程序在给定时间使用的带宽。但遗憾的是,如果您同时下载多个文件,那也会失败。

相关问题