Introduction
I'll explain how you can build a complete, reliable and powerful download manager with .NET Framework.
It is able to show a rarity of data about the download in progress, including the download speed, the file size, the downloaded size, and the percentage of completion.
A completely new class has been written based on this code, which features logic/layout abstraction, multiple file download support, and is more easy to use by providing a variety of properties and events. Check out the FileDownloader article.
The files are downloaded via the HttpWebRequest
and HttpWebResponse
, and written to a file with the StreamWriter
. All this is done on a separate thread (with a BackgroundWorker
), so the application using this downloader won't freeze.
Using the Code
The demo application is pretty simple, and contains only the needed UI components for downloading a file. You can of course implement this downloader in your own application, or just use it to download in the background without any user interaction.
The most important code can be found in the DoWork
method of the BackgroundWorker
. First, an attempt will be made to establish a connection to the file. If this is successful, a script in the Do
loop will start downloading the file block by block, until there are no remaining bytes to be downloaded. Note that as soon as a block of data has been received, it's written to the local target file.
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim theResponse As HttpWebResponse
Dim theRequest As HttpWebRequest
Try
theRequest = WebRequest.Create(Me.txtFileName.Text)
theResponse = theRequest.GetResponse
Catch ex As Exception
MessageBox.Show("An error occurred while downloading file. _
Possible causes:" & ControlChars.CrLf & _
"1) File doesn't exist" & ControlChars.CrLf & _
"2) Remote server error", "Error", _
MessageBoxButtons.OK, MessageBoxIcon.Error)
Dim cancelDelegate As New DownloadCompleteSafe(AddressOf DownloadComplete)
Me.Invoke(cancelDelegate, True)
Exit Sub
End Try
Dim length As Long = theResponse.ContentLength
Dim safedelegate As New ChangeTextsSafe(AddressOf ChangeTexts)
Me.Invoke(safedelegate, length, 0, 0, 0)
Dim writeStream As New IO.FileStream(Me.whereToSave, IO.FileMode.Create)
Dim nRead As Integer
Dim speedtimer As New Stopwatch
Dim currentspeed As Double = -1
Dim readings As Integer = 0
Do
If BackgroundWorker1.CancellationPending Then
Exit Do
End If
speedtimer.Start()
Dim readBytes(4095) As Byte
Dim bytesread As Integer = theResponse.GetResponseStream.Read_
(readBytes, 0, 4096)
nRead += bytesread
Dim percent As Short = (nRead * 100) / length
Me.Invoke(safedelegate, length, nRead, percent, currentspeed)
If bytesread = 0 Then Exit Do
writeStream.Write(readBytes, 0, bytesread)
speedtimer.Stop()
readings += 1
If readings >= 5 Then
currentspeed = 20480 / (speedtimer.ElapsedMilliseconds / 1000)
speedtimer.Reset()
readings = 0
End If
Loop
theResponse.GetResponseStream.Close()
writeStream.Close()
If Me.BackgroundWorker1.CancellationPending Then
IO.File.Delete(Me.whereToSave)
Dim cancelDelegate As New DownloadCompleteSafe(AddressOf DownloadComplete)
Me.Invoke(cancelDelegate, True)
Exit Sub
End If
Dim completeDelegate As New DownloadCompleteSafe(AddressOf DownloadComplete)
Me.Invoke(completeDelegate, False)
End Sub
This code uses delegates to invoke methods on the main thread, and pass data about the download progress to them.
The code does not support FTP downloads, but can be easily modified to do this.
Resume Downloads
It is possible to modify the code to allow resuming downloads. Add this code before the first use of the HttpWebRequest
object.
theRequest.AddRange(whereYouWantToStart)
You'll also need to set the Position
property of the FileStream
instance to the position where you want to resume the download. So be sure you also save this before the download is cancelled.
Dim writeStream As New IO.FileStream(Me.whereToSave, IO.FileMode.Open)
writeStream.Position = whereYouWantToStart
Calculating Speed
The code contains built in download speed calculation, using the StopWatch
class. This will return another result every time 5 packages have been downloaded. Note that you can also manually calculate the speed, and use a timer to get data at a more regular interval.