Click here to Skip to main content
Click here to Skip to main content

Threaded WebDownload class with Progress Call-backs

By , 19 Jul 2002
 

Download in progress

Preface

Ever wanted your application to download something from the Internet? Here is an implementation of a C# class that performs an asynchronous download of a file (over HTTP) providing call-backs for both progress and to indicate when the download is complete.

Introduction

Recently I needed my application to be able to retrieve a graphic from a web-server. During the download the rest of the application should still operate with full functionality. Typically a placeholder graphic being used until the download had completed. As soon as the download is complete, the newly downloaded graphic would be used rather than the placeholder.

Because the interface can look a little weird until all the downloads are complete, the user really needs to be aware that this is something that will be rectified after the download is complete.

Framework Provided Solutions

Since .Net operates within a rich framework, there is a good chance that Microsoft have already provided a number of different classes to help:

The WebRequest and HttpWebResponse classes allows a really simple download function to be implemented in just a few lines of code. Indeed, the following code worked for a while:

public Image GetImage(string urlFile)
{
    WebRequest wreq = WebRequest.Create( urlFile );
    HttpWebResponse httpResponse = (HttpWebResponse) wreq.GetResponse();
    Stream stream = httpResponse.GetResponseStream();
    return Image.FromStream(stream);
}

The problem with the above code was that with a large download or a slow web-site, the GetImage function above both locks the main application thread of my program and provides no download progress to the user. Obviously, there were still some options:

  • Use a new thread to perform the download, thus leaving my main thread free to work;
  • Use the Asynchronous stream mechanisms;

Existing Implementations

I found a couple of VB.NET examples that implemented the Asynchronous download of the file, however they still had some problems:

  1. After starting the asynchronous download, they had to wait at a ManualResetEvent to await the download to complete;
  2. They did not display any download progress;
  3. The used the StringBuilder class to store the download.

The final point was my biggest problem, with the conversion from bytes to chars would render the download pointless, trimming out important information from the download making construction of the image impossible.

My Requirements

  1. Shall not block operation with application (including its functionality, not simply the user-interface);
  2. Shall be capable of providing some progress indication;
  3. Shall notify when the download is complete;
  4. Shall preserve all of the downloaded information;
  5. Shall cope with unknown file sizes (Note, all information is stored in memory, so there is a limit to file sizes);

The Solution

Shown below are the classes implemented to satisfy my requirements above.

The DownloadThread class simply wraps my download thread and provides a logical place to store the DownloadCompleteHandler. It is also responsible for instantiating the WebDownload class and calling its Download method.

The DownloadInfo class is a simple class storing the download information, this information can be passed around within the asynchronous calls, providing access to the overall download (not just the snip that the call-back is informed).

The WebDownload class wraps the asynchronous download functionality with two call-backs,

  • Download is the main function, it initiates the download and then waits for the download to complete. Depending on what type of buffers are used, this function may also construct a byte[] return type from the working buffer;
  • ResponseCallback is called by the .Net Framework when the initial response from the web-server has been received. At this point, it is possible to look at the HTTP headers and determine if the web-server has specified the size of the file. If so, a byte array (byte[]) is created of the correct size. This is flagged in the DownloadInfo class with useFastBuffers set to true. If the file size is not known, slow buffers are used (which is a System.Collection.ArrayList allowing the contents to grow during the download);
  • ReadCallback is called whenever the read-buffer (set to 1k) fills up, at this point, the master-buffer gains this new read-buffer and the progress call-back is called.

Usage

The included tester application simply uses the following to start a download.

if ( this.downloadUrlTextBox.Text != "" )
{
  this.outputGroupBox.Enabled = true;

  // Initialize all form information
  this.bytesDownloadedTextBox.Text = "";
  this.totalBytesTextBox.Text = "";
  this.progressBar.Minimum = 0;
  this.progressBar.Maximum = 0;
  this.progressBar.Value = 0;

  // Create the download thread (class) and populate with filename and callbacks
  DownloadThread dl = new DownloadThread();
  dl.DownloadUrl = this.downloadUrlTextBox.Text;
  dl.CompleteCallback += new DownloadCompleteHandler( DownloadCompleteCallback );
  dl.ProgressCallback += new DownloadProgressHandler( DownloadProgressCallback );

  // Start the download thread....
  System.Threading.Thread t = new System.Threading.Thread( 
    new System.Threading.ThreadStart(
      dl.Download ));
  t.Start();
}

The tester application also changes its appearance slightly when the progress call-back indicates that the total file size is unknown. It hides the progress bar and places "Total File Size Not Known" within the Total Bytes display. The Bytes Downloaded display still counts up with the progress so far.

Known Issues

An OutOfMemoryException is raised when very large files are downloaded (40Mb caused it in my case, although this corresponded with my system running out of virtual memory).

Conclusion

Currently, this class exists within quite a safe environment. It is hoped that by sharing the code here, not only will others benefit from my efforts but also, with wider testing, any bugs can be discovered sooner.

Disclaimer

These classes have been developed for a prototype application only, if you use them, it is your responsibility to test them against your requirements.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ray Hayes
Product Manager
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralExcellent ArticlememberRyan Bost10 Oct '05 - 11:23 
GeneralProblems with proxymemberAlesBoldrik8 Aug '05 - 2:40 
GeneralRe: Problems with proxysussAnonymous8 Aug '05 - 6:55 
GeneralRe: Problems with proxymembern_dru15 Oct '07 - 22:03 
GeneralBeginInvokememberM.C.K.8 Jun '05 - 0:49 
GeneralRe: BeginInvokememberRay Hayes8 Jun '05 - 1:01 
QuestionHow to implement with Compact Framework?memberTravisSmith23 Feb '05 - 22:27 
AnswerRe: How to implement with Compact Framework?memberRay Hayes23 Feb '05 - 22:49 
QuestionFilename?memberTyronX8 Jan '05 - 12:23 
AnswerRe: Filename?memberRay Hayes9 Jan '05 - 6:26 
GeneralRe: Filename?memberTyronX10 Jan '05 - 8:39 
AnswerRe: Filename?memberwefra29 Oct '05 - 20:00 
GeneralRe: Filename?memberDaltonic2 Nov '05 - 1:01 
GeneralDownload file from FTP sitememberCasp14 Dec '04 - 4:18 
GeneralXcelent Article: Some Improvement SuggestionsmemberShay Ben-Sasson3 Dec '04 - 23:02 
GeneralCancelsussAnonymous17 Nov '04 - 1:26 
GeneralRe: CancelmemberMember 120782623 Dec '09 - 21:30 
GeneralASP.Net versionmembernormanordas30 Sep '04 - 1:38 
GeneralVB.Net Versionmemberevb17 Jun '04 - 10:05 
GeneralRe: VB.Net VersionmemberRay Hayes17 Jun '04 - 22:17 
QuestionWhy use the async methods?memberjlewin14 Feb '04 - 14:21 
AnswerRe: Why use the async methods?memberRay Hayes15 Feb '04 - 22:43 
GeneralBroken downloadsmemberelraton16 Jan '04 - 10:26 
GeneralRe: Broken downloadsmemberRay Hayes18 Jan '04 - 22:19 
GeneralRe: Broken downloadsmemberelraton19 Jan '04 - 9:12 
GeneralRe: Broken downloadsmemberRay Hayes20 Jan '04 - 0:00 
GeneralRe: Broken downloadsmemberelraton20 Jan '04 - 9:42 
GeneralRe: Broken downloadsmemberRay Hayes20 Jan '04 - 21:35 
GeneralRe: Broken downloadsmemberelraton21 Jan '04 - 9:42 
GeneralRe: Broken downloadsmemberRay Hayes21 Jan '04 - 21:33 
QuestionHow do I free the memory??memberAnders Munk8 Jan '04 - 22:19 
AnswerRe: How do I free the memory??memberRay Hayes8 Jan '04 - 22:50 
GeneralAsynchronous I/Omembercaseylara14 Sep '03 - 11:43 
GeneralBug: If the remote file doesn't exist...membertsigo8 Sep '03 - 14:05 
GeneralRe: Bug: If the remote file doesn't exist...membertsigo8 Sep '03 - 14:50 
GeneralRegarding File Sizes...membericonglyph8 Sep '03 - 6:35 
QuestionWhere is the downloaded file?memberManster1 Aug '03 - 4:30 
AnswerRe: Where is the downloaded file?memberRay Hayes1 Aug '03 - 4:38 
GeneralRe: Where is the downloaded file?memberManster1 Aug '03 - 6:28 
GeneralRe: Where is the downloaded file?membertsigo8 Sep '03 - 14:01 
GeneralRe: Where is the downloaded file?memberfredza7 Dec '04 - 7:57 
GeneralInvoke required for guimemberJohnno20 Jul '03 - 21:45 
GeneralRe: Invoke required for guimembertabatabaye23 Feb '04 - 18:48 
GeneralKristianmemberkriki29 Jun '03 - 22:55 
QuestionSeveral Threads ?memberMarc Schneider11 Jun '03 - 8:42 
AnswerRe: Several Threads ?memberRay Hayes11 Jun '03 - 22:47 
GeneralRe: Several Threads ?sussAnonymous11 Jun '03 - 23:58 
GeneralRe: Several Threads ?memberRay Hayes12 Jun '03 - 0:16 
GeneralRe: Several Threads ?memberMarc Schneider12 Jun '03 - 6:43 
GeneralRe: Several Threads ?memberJeffrey Scott Flesher9 Feb '04 - 9:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 20 Jul 2002
Article Copyright 2002 by Ray Hayes
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid