Ir para o conteúdo

Download assíncrono de ficheiro

Um simples cliente de web para download de ficheiros com informação de progresso detalhada.

Janela do programa de download de ficheiros, a descarregar um ficheiros

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