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.

H H
  • 263,252
  • 30
  • 330
  • 514
Agni451
  • 31
  • 1
  • 2
  • 2
    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()`? – jtbandes Aug 14 '11 at 23:17
  • 4
    you probably would have to end the http connection and reestablish it on resume. TCP will time out otherwise. – sleeplessnerd Aug 14 '11 at 23:20
  • 1
    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. – James Black Aug 14 '11 at 23:38
  • 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 HTTP – Tejs Aug 15 '11 at 03:18

1 Answers1

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.

Serge
  • 3,986
  • 2
  • 17
  • 37
  • 1
    Please, don't advise others to use `Thread.Abort()` unless completely necessary. That method should be really avoided. – svick Jan 29 '12 at 02:08
  • 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. – Serge Jan 29 '12 at 02:29
  • 1
    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. – Andrew Barber Jan 29 '12 at 06:24
  • 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. – Serge Jan 29 '12 at 06:31