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

C# .NET Background File Downloader

, 2 May 2009
Rate this:
Please Sign up or sign in to vote.
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.

// 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:

// 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:

private void btnPause_Click(object sender, RoutedEventArgs e)
{
    // Pause the downloader
    downloader.Pause();
}
private void btnResume_Click(object sender, RoutedEventArgs e)
{
    // Resume the downloader
    downloader.Resume();
} 
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:

// 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:

// 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.

// 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.

// 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)

Share

About the Author

Jeroen De Dauw
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
Follow on   Twitter

Comments and Discussions

 
QuestionNeed some info PinmemberTridip Bhattacharjee27-Mar-13 8:03 
SuggestionFile In Use Exception [modified] Pinmemberwannabeuk11-Dec-12 3:55 
Questionwin 7 64-bit freezes PinmemberNoel Buenaventura2-Dec-12 8:27 
Questiondownloader class Pinmembereranotz5017-Nov-12 5:46 
Questionhow to auto connect with internt Url Pinmembermanojmadhur28-Apr-12 10:20 
GeneralGood work Pinmemberjirikadlec230-May-11 10:41 
GeneralDoes not work. PinmemberiJam_j6-Feb-11 22:22 
GeneralRe: Does not work. PinmemberJeroen De Dauw7-Feb-11 5:49 
GeneralRe: Does not work. PinmemberiJam_j7-Feb-11 19:20 
GeneralAdded support for more complex urls [modified] PinmemberQRoX29-Jun-10 11:38 
GeneralA little improvement / bug fix PinmemberQRoX29-Jun-10 8:14 
GeneralFTP Enhancement PinmemberMustafa Tümer TAN25-Apr-10 0:42 
GeneralUnable to read data from the transport connection: The connection was closed PinmemberIzisin26-Mar-10 13:54 
GeneralRe: Unable to read data from the transport connection: The connection was closed Pinmemberjeroen de dauw26-Mar-10 19:04 
GeneralRe: Unable to read data from the transport connection: The connection was closed Pinmemberswaby21-Mar-12 17:06 
GeneralThanks for the article. Pinmemberoddessa3714-Jan-10 0:34 
GeneralRe: Thanks for the article. Pinmemberjeroen de dauw14-Jan-10 1:30 
QuestionRe: Thanks for the article. [modified] Pinmemberoddessa3714-Jan-10 4:12 
Generalexception Pinmembermalac13-Jul-09 2:16 
GeneralRe: exception Pinmemberbn2vs14-Jul-09 12:29 
QuestionDownload HTML file from web? PinmemberMicrotoby29-Jun-09 7:53 
AnswerRe: Download HTML file from web? Pinmemberbn2vs14-Jul-09 12:31 
GeneralRe: Download HTML file from web? PinmemberMicrotoby15-Jul-09 22:53 
GeneralRe: Download HTML file from web? Pinmemberbn2vs17-Jul-09 9:07 
QuestionWhy not use BITS? PinmemberSoundman32.24-May-09 21:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    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 | Mobile
Web04 | 2.8.140821.2 | Last Updated 2 May 2009
Article Copyright 2009 by Jeroen De Dauw
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid