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. Note that there is also a C# implementation of this class available.
Background
I started working on this class after someone on a programming help forum asked how to best download files in the background. I got a lot of ideas and based part of my code on the Downloading Files in .NET with all Information article, by Carmine_XX. This class adds multiple file support, logic/layout abstraction and is designed to be easy to use.
Using the Code
Once you added the class to your project, you should be able to access it via the namespace Bn.Classes
. You can of course adapt the namespace to your own preferences or delete it altogether.
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.
Private WithEvents downloader As New FileDownloader
Private Sub btnStart_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnStart.Click
Dim openFolderDialog As New FolderBrowserDialog
With downloader
If openFolderDialog.ShowDialog() = Windows.Forms.DialogResult.OK Then
.Files.Clear()
.LocalDirectory = openFolderDialog.SelectedPath
For Each path As String In txtFilesToDownload.Lines
.Files.Add(New FileDownloader.FileInfo(path))
Next
.Start()
End If
End With
End Sub
The code needed to then pause, resume or cancel the downloads couldn't be simpler:
Private Sub btnPauze_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnPauze.Click
downloader.Pause()
End Sub
Private Sub btnResume_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnResume.Click
downloader.Resume()
End Sub
Private Sub btnStop_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnStop.Click
downloader.Stop()
End Sub
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:
Private Sub downloader_StateChanged() _
Handles downloader.StateChanged
btnStart.Enabled = downloader.CanStart
btnStop.Enabled = downloader.CanStop
btnPauze.Enabled = downloader.CanPause
btnResume.Enabled = downloader.CanResume
txtFilesToDownload.ReadOnly = downloader.IsBusy
cbUseProgress.Enabled = Not downloader.IsBusy
End Sub
This is the demo code to display the progress information:
Private Sub downloader_ProgressChanged() Handles downloader.ProgressChanged
pBarFileProgress.Value = CInt(downloader.CurrentFilePercentage)
lblFileProgressDetails.Text = 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 Then
pBarTotalProgress.Value = CInt(downloader.TotalPercentage)
lblTotalProgressDetails.Text = _
String.Format("Downloaded {0} of {1} ({2}%)", _
FileDownloader.FormatSizeBinary(downloader.TotalProgress), _
FileDownloader.FormatSizeBinary(downloader.TotalSize), _
downloader.TotalPercentage)
End If
End Sub
Another noteworthy snippet of code is how to set the SupportsProgress
property.
Private Sub cbUseProgress_CheckedChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles cbUseProgress.CheckedChanged
downloader.SupportsProgress = cbUseProgress.Checked
End Sub
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 number 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.
Private Sub downloader_CalculationFileSize(ByVal sender As Object, _
ByVal fileNumber As Int32) Handles downloader.CalculatingFileSize
lblStatus.Text = String.Format("Calculating file sizes - _
file {0} of {1}", fileNumber, downloader.Files.Count)
End Sub
Points of Interest
When I started working on this class, I assumed it would be ready after working on it for a day. I ran into some problems though, involving a wrong assumption I had about multithreading, and the fact that you can only have two open HttpWebResponse
s at the same time. Before I created this class, I was oblivious to the fact that objects get locked and can't always be set via all threads without the proper extra code. I also never used the ReportProgress
event of the BackGroundWorker
. After creating this class, I did some more reading on multithreading to get a better grip on it, and I can recommend the following series of articles: Beginners Guide To Threading In .NET Part 1 of n. I hope this article will both help other people out, and receive some critic feedback on how to improve the class itself.
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 20th 2009: Published version 1.0.3 and updated this article
- April 30th 2009: Published version 1.0.2 on my forums (added
PackageSize
and StopWatchCyclesAmount
properties)
- April 22nd 2009: Published the first version of this article
- April 21st 2009: Published the class
- For the code this is based upon (can be seen as an older version), see this article.
References
- Dutch support for this class can be found here.
- If I have only slightly updated code, I won't update this article and only publish it here.