Pausing a download thread
Asked Answered
D

1

3

I'm writing a very simple bulk download program in c# that reads a .txt file of URLs to download. I've set it up with a global Thread and delegate for updating the GUI, and the pressing of the "Begin" button creates and starts that thread. What I want to do is have a "Pause" button that enables me to pause the download until I hit the "Resume" button. How do I do this?

The relevant code:

private Thread thr;
private delegate void UpdateProgressCallback(int curFile);

private void Begin_btn_Click(object sender, EventArgs e)
{
   thr = new Thread(Download);
   thr.Start();
}

private void Pause_btn_Click(object sender, EventArgs e)
{
   Pause_btn.Visible = false;
   Resume_btn.Visible = true;
   //{PAUSE THREAD thr}
}

private void Resume_btn_Click(object sender, Eventargs e)
{
   Pause_btn.Visible = true;
   Resume_btn.Visible = false;
   //{RESUME THREAD thr}
}

public void Download()
{
   //Download code goes here
}

Obviously, I am NOT using a Worker, and I really don't wish to unless you can show me how to get it to work (I don't really understand workers). Any help would be appreciated.

Demonetize answered 14/8, 2011 at 23:7 Comment(4)
I'm no C# expert, but I believe this will highly depend on how you are downloading the file. What's the actual code in Download()?Spunky
you probably would have to end the http connection and reestablish it on resume. TCP will time out otherwise.Affected
If you stop the download, then need to restart you will need to also keep track of where you stopped and perhaps download and ignore those parts, or start your download all over again.Push
The only way I can see this working is if you download in frames; and you loop while you dont have all the frames which allows you to pause downloading or resume downloading the next frame. Which basically means you wont be using HTTPExiguous
V
2

If you use System.Net.WebClient.DownloadFile() or System.Net.WebClient.DownloadFileAsync() method then you cannot pause the download. The difference between these methods is that the latter method will start an asynchronous download so you will not need to create a separate thread yourself if you use this method. Unfortunately, downloads executed with either method cannot be paused or resumed.

You need to use System.Net.HttpWebRequest. Try something like this:

class Downloader
{
    private const int chunkSize = 1024;
    private bool doDownload = true;

    private string url;
    private string filename;

    private Thread downloadThread;

    public long FileSize
    {
        get;
        private set;
    }
    public long Progress
    {
        get;
        private set;
    }

    public Downloader(string Url, string Filename)
    {
        this.url = Url;
        this.filename = Filename;
    }

    public void StartDownload()
    {
        Progress = 0;
        FileSize = 0;
        commenceDownload();
    }

    public void PauseDownload()
    {
        doDownload = false;
        downloadThread.Join();
    }

    public void ResumeDownload()
    {
        doDownload = true;
        commenceDownload();
    }

    private void commenceDownload()
    {
        downloadThread = new Thread(downloadWorker);
        downloadThread.Start();
    }

    public void downloadWorker()
    {
        // Creates an HttpWebRequest with the specified URL. 
        HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);

        FileMode filemode;
        // For download resume
        if (Progress == 0)
        {
            filemode = FileMode.CreateNew;
        }
        else
        {
            filemode = FileMode.Append;
            myHttpWebRequest.AddRange(Progress);
        }

        // Set up a filestream to write the file
        // Sends the HttpWebRequest and waits for the response.         
        using (FileStream fs = new FileStream(filename, filemode))
        using (HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse())
        {
            // Gets the stream associated with the response.
            Stream receiveStream = myHttpWebResponse.GetResponseStream();

            FileSize = myHttpWebResponse.ContentLength;

            byte[] read = new byte[chunkSize];
            int count;


            while ((count = receiveStream.Read(read, 0, chunkSize)) > 0 && doDownload)
            {
                fs.Write(read, 0, count);
                count = receiveStream.Read(read, 0, chunkSize);

                Progress += count;
            }
        }
    }
}

I used some code from HttpWebRequest.GetResponse page on MSDN.

Instead of stopping the thread on Pause and starting a new one on Resume, you can also change the while loop to wait until download is resumed as following:

            while ((count = receiveStream.Read(read, 0, chunkSize)) > 0)
            {
                fs.Write(read, 0, count);
                count = receiveStream.Read(read, 0, chunkSize);

                Progress += count;

                while(!doDownload)
                    System.Threading.Thread.Sleep(100);
            }

The up-side is that you may be able to re-use the same thread. The down-side is that the connection may timeout and become closed. In the latter case, you will need to detect this and re-connect.

You may also want to add an event for when the donwload is completed.

Visby answered 29/1, 2012 at 1:50 Comment(4)
Please, don't advise others to use Thread.Abort() unless completely necessary. That method should be really avoided.Alphanumeric
I do not know of any other way to do what the poster wants. I am not aware of any managed way to get n bytes from a web stream (synchronously or otherwise). Please correct me if I am wrong.Visby
You are wrong. You need to access the stream, then it is trivial to stop pulling down bytes and stop/pause the reading at any moment. Thread.Abort() is almost always a bad idea.Oriflamme
I checked myself and found that HttpWebResponse.GetResponseStream() does return a stream. I know that Thread.Abort() is a bad idea, just couldn't find a Stream to read. I will amend my answer.Visby

© 2022 - 2024 — McMap. All rights reserved.