Click here to Skip to main content
12,076,727 members (44,978 online)
Click here to Skip to main content
Add your own
alternative version

Stats

59.2K views
2.5K downloads
100 bookmarked
Posted

Downloader Component

, 14 Jan 2007
Rate this:
Please Sign up or sign in to vote.
A component to download files over the network with support for proxies, SSL and resume.
Sample Image - Downloader.jpg

Introduction

Downloading files over the network could be a cumbersome task. Trying to implement other features like auto-resume and support for proxy servers, etc. makes it even harder. Using this component can make things a lot easier for yourself. You can download files over proxies, password protected sites or untrusted SSL connections simply by setting a few properties. The example that is provided shows how to do the job and how to handle events over a simple GUI.

Since the download operation takes place in a worker thread asynchronously, you don't need to worry about downloading large files or blocking the user interface. There are some points you need to have in mind though.

Aborting the Worker Thread

Stopping a download operation that is running is tricky. You could just call the DownloadThread.Abort() method on the downloader thread to stop the work, but this is not the recommended way. The main reason for this is that Thread.Abort throws an exception which can occur at any time during your work and leaves your running state inconsistent. The easier way is to check for a boolean field, and terminate the operation when the field is set, or run the job on an entirely different process. To read more about this idea, check Ian Griffiths blog post here.

Updating GUI

When running a download job, the Downloader component provides its internal progress and error state through events and custom eventargs. Since the eventargs are constructed on the worker thread, trying to update the user-interface from the worker thread throws a Cross-Thread exception. The workaround to update the user interface is to use the Invoke method (inherited from Control), a Delegate and call to the necessary method(s), like this:

private delegate void ParamMethodInvoker
    (long fileSize, long progressValue, string message);

/// <summary>
/// Call UpdateProgress method using a delegate and provide method parameters
/// </summary>
private void OnDownloadProgressChanged
    (object sender, DownloadProgressEventArgs e)
{
    Invoke(new ParamMethodInvoker(UpdateProgress), new object[] 
        { e.BytesRead, e.TotalBytes, e.Message });
}

/// <summary>
/// Normally update the user interface
/// </summary>
private void UpdateProgress(long bytesRead, long totalBytes, string message)
{
    int kbRead = Convert.ToInt32(bytesRead) / 1024;
    int kbTotal = Convert.ToInt32(totalBytes) / 1024;
    int percent = Convert.ToInt32((bytesRead * 100) / totalBytes);

    progress.Value = percent;
    lblDownloadMessage.Text = 
        string.Format("{0:#,###} of {1:#,###} KBytes ({2}%)", 
        kbRead, kbTotal, percent);
}

Resuming a Download

To resume a download job, we can simply continue where we left off. When restarting a download job, the downloader component first checks for the existing file at the specified location (through DownloadPath property). If the file exists, it reads the Length of the existing file and sets the HttpWebRequest's range using AddRange method. Overloads of the AddRange method could be used to handle multi-part downloading of a single file.

long startingPoint = 0;

if(File.Exists(DownloadPath))
{
    startingPoint = new FileInfo(DownloadPath).Length;
}

HttpWebRequest _Request = (HttpWebRequest)HttpWebRequest.Create(url);
_Request.AddRange(startingPoint);

Points of Interest

Implementing other features such as multi-part downloading (like download accelerator applications) and FTP downloads seems like a good idea. Any suggestions/comments/feedback are highly appreciated.

History

Version 1

  • Provided the component with base features and events
  • Provided a basic form to show features of the component

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Hadi Eskandari
Software Developer (Senior) Readify
Australia Australia
Working on both Java and .NET Technology, I have developed various enterprise level applications on both platforms. Currently, I am working as a Senior Software Developer at Readify which is a leading company on .NET technology in Australia.

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
Michael Haephrati7-Mar-13 12:03
mvpMichael Haephrati7-Mar-13 12:03 
QuestionMy vote of 5 Pin
Pouriya.GH30-Apr-12 4:27
memberPouriya.GH30-Apr-12 4:27 
GeneralMy vote of 5 Pin
Shahin Khorshidnia3-Apr-11 20:18
memberShahin Khorshidnia3-Apr-11 20:18 
GeneralError Downloading file greater than 2GB [modified] Pin
akshayoh12-Feb-11 9:43
memberakshayoh12-Feb-11 9:43 
GeneralRe: Error Downloading file greater than 2GB [modified] Pin
KOS_void28-Feb-12 5:39
memberKOS_void28-Feb-12 5:39 
Generaldoc required Pin
prudhvi1234516-Mar-10 10:03
memberprudhvi1234516-Mar-10 10:03 
GeneralImplementing Speed Limiting (aka Throttling/Shaping) Pin
TrendyTim14-Sep-07 17:30
memberTrendyTim14-Sep-07 17:30 
I wanted to use your code to download in the background while still allowing the internet to be responsive and i finally figured out how to limit the download speeds, it may turn out its not a one size fits all solution but it in limited testing (2 machines on opposite sides of the world, 1 remote file and results mirror each other) it looks good.

The gist of the solution is to have the thread sleep after writing the chunk to disk, delaying its next read.

I also made a change so that instead of calling _SaveFileStream.Length every time for the progress changed, it keeps a running tally as the .Length causes it to check the file size from the file system which i would presume is a little less efficient than incrementing and checking a variable.

With a chunk/packet length/buffer size (whatever you want to call it) of 2048 the following is true on my 512k DSL with a normal speed of around 35kb/s before the speed limit is enabled.

100ms = 20kb/s
200ms = 10kb/s


On a connection where the normal download speed is in excess of 200kbs for the same file.
25ms = 80kb/s
100ms = 20kb/s
200ms = 10kb/s

So clearly sleep time is independent of connection speed which makes it a feasible method.

When halving the chunk size from 2048 to 1024, the KB/s rate halves for the same sleep time, so for limiting to a speed > 320k it would probably require a larger chunk size (though it would start to loose accuracy after 80k since you cant sleep for 12.5ms, unless you alternate between 12ms and 13ms)


I have used NetLimiter to monitor the download speeds of the application and it appears to be mostly accurate.

The only problem is i haven't yet figured out how to create a formula from that for converting a speed limit into a sleep time/chunk size and for now limiting to 20kb/s is good enough, perhaps if what i have written makes sense to someone (i daresay it could be explained better) they might be able to help in that regard.


And thanks for the initial code Hadi.
Generalpoint of interest Pin
jugomkd7510-Aug-07 9:25
memberjugomkd7510-Aug-07 9:25 
GeneralRe: point of interest Pin
jugomkd7510-Aug-07 22:20
memberjugomkd7510-Aug-07 22:20 
QuestionHow about FTP ?? Pin
_Thurein_9-May-07 18:09
member_Thurein_9-May-07 18:09 
AnswerRe: How about FTP ?? Pin
h.eskandari9-May-07 20:52
memberh.eskandari9-May-07 20:52 
GeneralRe: How about FTP ?? Pin
_Thurein_9-May-07 22:36
member_Thurein_9-May-07 22:36 
GeneralRe: How about FTP ?? Pin
h.eskandari9-May-07 23:41
memberh.eskandari9-May-07 23:41 
GeneralRe: How about FTP ?? Pin
Gaara [BTK]12-Oct-07 1:28
memberGaara [BTK]12-Oct-07 1:28 
Questionwhat if from a webdav server Pin
roychoo16-Mar-07 5:11
memberroychoo16-Mar-07 5:11 
AnswerRe: what if from a webdav server Pin
Hadi Eskandari16-Mar-07 19:51
memberHadi Eskandari16-Mar-07 19:51 
GeneralTest for file size if resuming Pin
Jeffrey Scott Flesher23-Jan-07 19:14
memberJeffrey Scott Flesher23-Jan-07 19:14 
GeneralVery similar... Pin
Scott Dorman15-Jan-07 7:30
memberScott Dorman15-Jan-07 7:30 
GeneralRe: Very similar... Pin
Hadi Eskandari16-Jan-07 20:22
memberHadi Eskandari16-Jan-07 20:22 
GeneralRe: Very similar... Pin
Scott Dorman18-Jan-07 5:09
memberScott Dorman18-Jan-07 5:09 
GeneralTnx For This Article Pin
yunas14-Jan-07 3:36
memberyunas14-Jan-07 3:36 
GeneralJust a little problem Pin
Vertyg014-Jan-07 2:34
memberVertyg014-Jan-07 2:34 
GeneralRe: Just a little problem Pin
Hadi Eskandari14-Jan-07 20:14
memberHadi Eskandari14-Jan-07 20:14 
GeneralRe: Just a little problem Pin
Hadi Eskandari11-Feb-07 21:50
memberHadi Eskandari11-Feb-07 21:50 
GeneralRe: Just a little problem Pin
Vertyg011-Feb-07 22:42
memberVertyg011-Feb-07 22:42 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160212.1 | Last Updated 14 Jan 2007
Article Copyright 2007 by Hadi Eskandari
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid