Click here to Skip to main content
15,867,568 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

 
GeneralRe: exception Pin
Jeroen De Dauw14-Jul-09 12:29
Jeroen De Dauw14-Jul-09 12:29 
QuestionDownload HTML file from web? Pin
Microtoby29-Jun-09 7:53
Microtoby29-Jun-09 7:53 
AnswerRe: Download HTML file from web? Pin
Jeroen De Dauw14-Jul-09 12:31
Jeroen De Dauw14-Jul-09 12:31 
GeneralRe: Download HTML file from web? Pin
Microtoby15-Jul-09 22:53
Microtoby15-Jul-09 22:53 
GeneralRe: Download HTML file from web? Pin
Jeroen De Dauw17-Jul-09 9:07
Jeroen De Dauw17-Jul-09 9:07 
QuestionWhy not use BITS? Pin
Soundman32.24-May-09 21:26
Soundman32.24-May-09 21:26 
AnswerRe: Why not use BITS? Pin
Jeroen De Dauw4-May-09 21:37
Jeroen De Dauw4-May-09 21:37 

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.