Download assíncrono de ficheiro
Um simples cliente de web para download de ficheiros com informação de progresso detalhada.
Consiste em duas classes:
Downloader.vb
'-----------------------------------------------------'
' Downloader Demo '
' c0d3d bY fLaSh '
'-----------------------------------------------------'
' 07/2009 - c4rl0s.pt [at] gmail [dot] com '
'-----------------------------------------------------'
Imports System.Net
Imports System.ComponentModel
Public Class Downloader
''' <summary>Public events</summary>
Public Event CompleteCallback(ByVal sender As Object, ByVal e As AsyncCompletedEventArgs)
Public Event ProgressCallback(ByVal sender As Object, ByVal e As DownloadProgressEventArgs)
''' <summary>Private attributes</summary>
Private __WebClient As WebClient
Private __Destination As String
Private __DownloadURL As String
Private __StartedTime As DateTime
''' <summary>Properties</summary>
Friend ReadOnly Property DownloadURL() As String
Get
Return Me.__DownloadURL
End Get
End Property
Friend ReadOnly Property Destination() As String
Get
Return Me.__Destination
End Get
End Property
Friend ReadOnly Property WebClient() As WebClient
Get
Return __WebClient
End Get
End Property
''' <summary>
''' Inicia o donwload de um determinado ficheiro por HTTP..
''' </summary>
''' <param name="downloadURL">A origem do download</param>
''' <param name="destination">O path para o destino do ficheiro</param>
''' <param name="sErrDesc">Opcional, no caso de erro defeine a string com a exepcao</param>
''' <param name="oWebClient">Opcional, no caso de por ex. ser necessario defenir o Proxy</param>
Public Function StartDownload(ByVal downloadURL As String, _
ByVal destination As String, _
Optional ByRef sErrDesc As String = "", _
Optional ByVal oWebClient As WebClient = Nothing) As Boolean
Try
'Cria nova instancia do objecto..
If oWebClient Is Nothing Then
__WebClient = New WebClient
Else
__WebClient = oWebClient
End If
'Define as variaveis
__Destination = destination
__DownloadURL = downloadURL
__StartedTime = Now
'Connecta ao gestor de envetos
AddHandler __WebClient.DownloadFileCompleted, AddressOf DownloadCompleteHandler
AddHandler __WebClient.DownloadProgressChanged, AddressOf DownloadProgressChangedHandler
'Inicia o download..
__WebClient.DownloadFileAsync(New Uri(__DownloadURL), __Destination)
Return True
Catch ex As Exception
sErrDesc = ex.Message
End Try
End Function
''' <summary>
''' Cacela do download
''' </summary>
Public Sub Cancel()
If __WebClient IsNot Nothing Then
__WebClient.CancelAsync()
__WebClient.Dispose()
End If
End Sub
''' <summary>Private events, usados por o objecto __WebClient</summary>
Private Sub DownloadCompleteHandler(ByVal sender As Object, ByVal e As AsyncCompletedEventArgs)
RaiseEvent CompleteCallback(sender, e)
End Sub
Private Sub DownloadProgressChangedHandler(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
RaiseEvent ProgressCallback(sender, New DownloadProgressEventArgs(e, __StartedTime))
End Sub
End Class
DownloaderEventArgs.vb
'-----------------------------------------------------'
' Downloader Demo '
' c0d3d bY fLaSh '
'-----------------------------------------------------'
' 07/2009 - c4rl0s.pt [at] gmail [dot] com '
'-----------------------------------------------------'
Imports System.Net
Imports System.ComponentModel
''' <summary>
''' Esta class foi defenida para simplificar os argumentos do do evento 'DownloadProgressChangedEventArgs'
''' </summary>
Public Class DownloadProgressEventArgs
Private __DownloadProgressChangedEventArgs As DownloadProgressChangedEventArgs
Private __Started As DateTime
Public Sub New(ByVal o As DownloadProgressChangedEventArgs, ByVal dStarted As DateTime)
__DownloadProgressChangedEventArgs = o
__Started = dStarted
End Sub
''' <summary>
''' Retorna o objecto 'original' do evento
''' </summary>
Friend ReadOnly Property DownloadProgressChangedEventArgs() As DownloadProgressChangedEventArgs
Get
Return __DownloadProgressChangedEventArgs
End Get
End Property
''' <summary>
''' Retorna o total de bytes recebidos
''' </summary>
Friend ReadOnly Property BytesReceived() As String
Get
Return FormatBytes(__DownloadProgressChangedEventArgs.BytesReceived)
End Get
End Property
''' <summary>
''' Retorna o total de bytes do ficheiro do download
''' </summary>
Friend ReadOnly Property TotalBytes() As String
Get
Return FormatBytes(__DownloadProgressChangedEventArgs.TotalBytesToReceive)
End Get
End Property
''' <summary>
''' Retorna o total de bytes recebidos
''' </summary>
Friend ReadOnly Property TotalBytesToReceive() As String
Get
Return FormatBytes(__DownloadProgressChangedEventArgs.TotalBytesToReceive - __DownloadProgressChangedEventArgs.BytesReceived)
End Get
End Property
''' <summary>
''' Retorna a percentagem do progresso do download
''' </summary>
Friend ReadOnly Property Percentage() As Integer
Get
Return __DownloadProgressChangedEventArgs.ProgressPercentage
End Get
End Property
''' <summary>
''' Retorna o tempo decurrido formatado par 'mm:ss'
''' </summary>
Friend ReadOnly Property Elapsed() As String
Get
Dim oTimeSpan As TimeSpan = Date.op_Subtraction(Now, __Started)
Return FormatTimeSpan(oTimeSpan)
End Get
End Property
Friend ReadOnly Property Remain() As String
Get
Dim oTimeSpan As TimeSpan = Date.op_Subtraction(Now, __Started)
' É necessário fazer um regra 3 simples..
' Se demorou 30 segundos para baixar 600 kb, quanto tempo demora para baixar 1200 kb?
'600 kb _ 1200 kb
' 10 sec X sec
Dim dblRemainSeconds As Double = (oTimeSpan.TotalSeconds * _
__DownloadProgressChangedEventArgs.TotalBytesToReceive) / _
__DownloadProgressChangedEventArgs.BytesReceived
oTimeSpan = Date.op_Subtraction(Now.AddSeconds(dblRemainSeconds - oTimeSpan.TotalSeconds), Now)
Return FormatTimeSpan(oTimeSpan)
End Get
End Property
''' <summary>
''' Formata bytes apropriados perante o tamanho..
''' </summary>
Private Function FormatBytes(ByVal dblBytes As Double) As String
Const KILOBYTE As Double = 1024
Const MEGABYTE As Double = KILOBYTE ^ 2
Const GIGABYTE As Double = KILOBYTE ^ 3
Const TERABYTE As Double = KILOBYTE ^ 4
Const PETABYTE As Double = KILOBYTE ^ 5
Select Case dblBytes
Case Is >= PETABYTE
Return System.Math.Round(dblBytes / PETABYTE, 2) & " PiB"
Case Is >= TERABYTE
Return System.Math.Round(dblBytes / TERABYTE, 2) & " TiB"
Case Is >= GIGABYTE
Return System.Math.Round(dblBytes / GIGABYTE, 2) & " GiB"
Case Is >= MEGABYTE
Return System.Math.Round(dblBytes / MEGABYTE, 2) & " MiB"
Case Is >= KILOBYTE
Return System.Math.Round(dblBytes / KILOBYTE, 2) & " KiB"
Case Else
Return dblBytes & " Bytes"
End Select
End Function
''' <summary>
''' Formata TimeSpan 'para mm:ss'
''' </summary>
Private Function FormatTimeSpan(ByVal oTimeSpan As TimeSpan) As String
Return IIf(Round(oTimeSpan.TotalMinutes, 9999999) > 9, Round(oTimeSpan.TotalMinutes, 9999999), "0" & Round(oTimeSpan.TotalMinutes, 9999999)) & ":" & _
IIf(Round(oTimeSpan.TotalSeconds, 60) > 9, Round(oTimeSpan.TotalSeconds, 60), "0" & Round(oTimeSpan.TotalSeconds, 60))
End Function
''' <summary>
''' Arredonda digitos
''' </summary>
Private Function Round(ByVal d As Double, ByVal iMod As Integer) As Long
Return System.Math.Round(d, 0) Mod iMod
End Function
End Class
Exemplo de uso
Public Class Form1
Private WithEvents DL As New Downloader
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
DL.StartDownload("url", "destino")
End Sub
Private Sub DL_CompleteCallback(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs) Handles DL.CompleteCallback
MsgBox("Download completo!")
End Sub
Private Sub DL_ProgressCallback(ByVal sender As Object, ByVal e As DownloadProgressEventArgs) Handles DL.ProgressCallback
Debug.Print(e.BytesReceived & "/" & e.TotalBytesToReceive)
End Sub
End Class