Click here to Skip to main content
15,868,141 members
Articles / Programming Languages / C#

C# .NET Background File Downloader

Rate me:
Please Sign up or sign in to vote.
4.91/5 (24 votes)
2 May 2009GPL33 min read 135K   13.5K   106   33
A multithreaded file downloader with progress details, speed info and more
FileDownloaderDemo-downloading.gif

Introduction

This class enables you to easily download multiple files in the background (via a separate thread), and will provide information about the amount of downloaded data, the percentage of completion, and the download speed. On top of this, you can cancel downloading, pause it, and of course, also resume.

Background

I started working on this class after someone on a programming help forum asked how to best download files in the background. I originally wrote it in VB.NET, but have now created a C# implementation. Check the VB.NET Background File Downloader article for the original code. Another difference with the original VB.NET implementation is that this code uses a WPF demo application, and not a Windows Forms based one.

Using the Code

Once you added the class to your project, you should be able to access it via the project's namespace.

The first thing you need to do when using this class is (logically) create a new instance, and then add the files you want to download. You'll also need to set the local directory to which you want to download. This is pretty straight forward.

C#
// Creating a new instance of a FileDownloader
private FileDownloader downloader = new FileDownloader();

You'll also want to add some files to the list that need to be downloaded. This example demonstrates how to read each line of a WPF RichTextBox and add it to the list:

C#
// A simple implementation of setting the directory path, 
// adding files from a textbox and starting the download
private void btnStart_Click(object sender, RoutedEventArgs e)
{
    System.Windows.Forms.FolderBrowserDialog openFolderDialog = 
			new System.Windows.Forms.FolderBrowserDialog();
     if (openFolderDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    {
        // Set the path to the local directory where the files will be downloaded to
        downloader.LocalDirectory = openFolderDialog.SelectedPath;
         // Clear the current list of files (in case it's not the first download)
        downloader.Files.Clear();
         // Get the contents of the rich text box
        string rtbContents = new TextRange(rtbPaths.Document.ContentStart, 
					rtbPaths.Document.ContentEnd).Text;
        foreach (string line in rtbContents.Split('\n'))
        {
            String trimmedLine = line.Trim(' ', '\r');
            if (trimmedLine.Length > 0)
            {
                // If the line is not empty, assume it's a valid URL 
	       // and add it to the files list
                // Note: You could check if the URL is valid before adding it, 
	       // and probably should do this is a real application
                downloader.Files.Add(new FileDownloader.FileInfo(trimmedLine));
            }
        }
         // Start the downloader
        downloader.Start();
    }
}

Note: The example code in this article is for a C# WPF application, and will be slightly different for a C# forms application.

The code needed to then pause, resume or cancel the downloads couldn't be simpler:

C#
private void btnPause_Click(object sender, RoutedEventArgs e)
{
    // Pause the downloader
    downloader.Pause();
}
C#
private void btnResume_Click(object sender, RoutedEventArgs e)
{
    // Resume the downloader
    downloader.Resume();
} 
C#
private void btnStop_Click(object sender, RoutedEventArgs e)
{
    // Stop the downloader
    // Note: This will not be instantaneous - the current requests need to be 
    // closed down, and the downloaded files need to be deleted
    downloader.Stop();
} 

The downloader also provides a few properties to indicate its current state: IsBusy, IsPaused, CanStart, CanStop, CanPause and CanResume (all booleans). Here you have an example of how to use these to set your interface:

C#
// This event is fired every time the paused or busy state is changed, 
// and used here to set the controls of the interface
// This makes it equivalent to a void handling both 
// downloader.IsBusyChanged and downloader.IsPausedChanged
private void downloader_StateChanged(object sender, EventArgs e)
{
    // Setting the buttons
    btnStart.IsEnabled = downloader.CanStart;
    btnStop.IsEnabled = downloader.CanStop;
    btnPause.IsEnabled = downloader.CanPause;
    btnResume.IsEnabled = downloader.CanResume;
     // Enabling or disabling the setting controls
    rtbPaths.IsReadOnly = downloader.IsBusy;
    cbUseProgress.IsEnabled = !downloader.IsBusy;
} 

This is the demo code to display the progress information:

C#
// Occurs every time of block of data has been downloaded, 
// and can be used to display the progress with
// Note that you can also create a timer, 
// and display the progress every certain interval
// Also note that the progress properties return a size in bytes, 
// which is not really user friendly to display
// The FileDownloader class provides static functions to format 
// these byte amounts to a more readable format, either in binary or decimal notation 
private void downloader_ProgressChanged(object sender, EventArgs e)
{
    pBarFileProgress.Value = downloader.CurrentFilePercentage();
    lblFileProgress.Content = String.Format("Downloaded {0} of {1} ({2}%)", 
		FileDownloader.FormatSizeBinary(downloader.CurrentFileProgress), 
		FileDownloader.FormatSizeBinary(downloader.CurrentFileSize), 
		downloader.CurrentFilePercentage()) + String.Format(" - {0}/s", 
		FileDownloader.FormatSizeBinary(downloader.DownloadSpeed));
   
    if (downloader.SupportsProgress)
    {
        pBarTotalProgress.Value = downloader.TotalPercentage();
        lblTotalProgress.Content = String.Format("Downloaded {0} of {1} ({2}%)", 
		FileDownloader.FormatSizeBinary(downloader.TotalProgress), 
		FileDownloader.FormatSizeBinary(downloader.TotalSize), 
		downloader.TotalPercentage());
    }
} 

Another noteworthy snippet of code is how to set the SupportsProgress property.

C#
// Setting the SupportsProgress property - if set to false, 
// no total progress data will be available!
private void cbUseProgress_Checked(object sender, RoutedEventArgs e)
{
    downloader.SupportsProgress = (Boolean)cbUseProgress.IsChecked; 
} 

When the SupportProgress property is set to true, the file sizes will be calculated before any download is started. This can take a while, definitely when you have a large amount of files. The FileDownloader class fires an event every time it starts checking the size of a file, which can be used to display the progress.

C#
// Show the progress of file size calculation
// Note that these events will only occur when the total file size is 
// calculated in advance, in other words when the SupportsProgress is set to true
private void downloader_CalculationFileSize(object sender, Int32 fileNr)
{
    lblStatus.Content = String.Format("Calculating file sizes - 
			file {0} of {1}", fileNr, downloader.Files.Count);
}

FileDownloaderDemo-calculating.gif

Points of Interest

The main reasons for translating my VB.NET class to C# were to both fully familiarize myself with C#, and its differences compared to VB.NET, and to exercise some WPF basics.

I'm hoping to implement some more features soon, including cancellation without deleting the files and the option to resume downloading afterwards, and the ability to download multiple files simultaneously, on separate threads.

History

  • May 2nd 2009: Published the C# class and this article
  • April 22nd 2009: Published an article about the VB.NET class
  • April 21st 2009: Published the VB.NET class
    • For the code the original VB.NET class is based upon (can be seen as an older version), see this article.

References

  • Dutch support for this class can be found here.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer
Belgium Belgium
I am a free and open source software enthusiast and freelance software developer with multiple years of experience in both web and desktop development. Currently my primarily focus is on MediaWiki and Semantic MediaWiki work. I'm in the all time top 10 MediaWiki comitters and am one of the WikiWorks consultants. You can contact me at jeroendedauw at gmail for development jobs and questions related to my work.

More info can be found on my website [0] and my blog [1]. You can also follow me on twitter [2] and identi.ca [3].

[0] http://www.jeroendedauw.com/
[1] http://blog.bn2vs.com/
[2] https://twitter.com/#!/JeroenDeDauw
[3] http://identi.ca/jeroendedauw

Comments and Discussions

 
QuestionSimultaneous multiple file download throws operation timeout error Pin
Member 131066981-Jun-17 2:26
Member 131066981-Jun-17 2:26 
AnswerRe: Simultaneous multiple file download throws operation timeout error Pin
Member 131066985-Jun-17 1:07
Member 131066985-Jun-17 1:07 
QuestionNeed download time left during download processing Pin
Tran Alex (Official)23-Aug-16 4:50
Tran Alex (Official)23-Aug-16 4:50 
QuestionHave you implemented multi threading concept? Pin
manisandeep22-Jun-16 2:51
manisandeep22-Jun-16 2:51 
QuestionNeed some info Pin
Tridip Bhattacharjee27-Mar-13 8:03
professionalTridip Bhattacharjee27-Mar-13 8:03 
SuggestionFile In Use Exception Pin
wannabeuk11-Dec-12 3:55
wannabeuk11-Dec-12 3:55 
Questionwin 7 64-bit freezes Pin
Noel Buenaventura2-Dec-12 8:27
Noel Buenaventura2-Dec-12 8:27 
Questiondownloader class Pin
eranotz5017-Nov-12 5:46
eranotz5017-Nov-12 5:46 
Questionhow to auto connect with internt Url Pin
manojmadhur28-Apr-12 10:20
manojmadhur28-Apr-12 10:20 
GeneralGood work Pin
jirikadlec230-May-11 10:41
jirikadlec230-May-11 10:41 
GeneralDoes not work. Pin
iJam_j6-Feb-11 22:22
iJam_j6-Feb-11 22:22 
GeneralRe: Does not work. Pin
Jeroen De Dauw7-Feb-11 5:49
Jeroen De Dauw7-Feb-11 5:49 
GeneralRe: Does not work. Pin
iJam_j7-Feb-11 19:20
iJam_j7-Feb-11 19:20 
Works okay now. Dont know whats wrong.
GeneralAdded support for more complex urls [modified] Pin
QRoX29-Jun-10 11:38
QRoX29-Jun-10 11:38 
GeneralA little improvement / bug fix Pin
QRoX29-Jun-10 8:14
QRoX29-Jun-10 8:14 
GeneralFTP Enhancement Pin
Mustafa Tümer TAN25-Apr-10 0:42
Mustafa Tümer TAN25-Apr-10 0:42 
GeneralRe: FTP Enhancement Pin
amitt048818-May-16 5:45
amitt048818-May-16 5:45 
GeneralUnable to read data from the transport connection: The connection was closed Pin
Izisin26-Mar-10 13:54
Izisin26-Mar-10 13:54 
GeneralRe: Unable to read data from the transport connection: The connection was closed Pin
Jeroen De Dauw26-Mar-10 19:04
Jeroen De Dauw26-Mar-10 19:04 
GeneralRe: Unable to read data from the transport connection: The connection was closed Pin
swaby21-Mar-12 17:06
swaby21-Mar-12 17:06 
GeneralRe: Unable to read data from the transport connection: The connection was closed Pin
nv1367-Jul-16 18:13
nv1367-Jul-16 18:13 
GeneralThanks for the article. Pin
oddessa3714-Jan-10 0:34
oddessa3714-Jan-10 0:34 
GeneralRe: Thanks for the article. Pin
Jeroen De Dauw14-Jan-10 1:30
Jeroen De Dauw14-Jan-10 1:30 
QuestionRe: Thanks for the article. [modified] Pin
oddessa3714-Jan-10 4:12
oddessa3714-Jan-10 4:12 
Generalexception Pin
malac13-Jul-09 2:16
malac13-Jul-09 2:16 

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.