Download di file in .Net visualizzando tutte le informazioni (progressbar, riprendere download interrotti ecc…)
In questo articolo vi spiegherò come creare un semplice ma efficace download manager che mostra tutte le informazioni riguardo il download del file: la velocità di download, la dimensione del file, la progressbar con la percentuale di completamento e permette il pause&resume download.
Le basi per questo programma sono le classi WebRequest e WebResponse del namespace System.Net.
La classe WebRequest rappresenta un richiesta web che dopo eseguita ritorna un oggetto WebResponse che è lo stream di dati ricevuto in output.
Il programma contiene solamente una form con qualche label, una progressbar, e una textbox dove inserire l’ URL del file da scaricare. Ovviamente contiene anche due pulsanti (uno per avviare il download, uno per stopparlo).
Invece, il cuore dell’ applicazione è in un BackgroundWorker in modo l’ UI non si freeza durante il download.
Questo è il codice contenuto nell’ evento BackgroundWorker_DoWork:
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) _ Handles BackgroundWorker1.DoWork 'Crea la richiesta ed ottiene la risposta Dim theResponse As HttpWebResponse Dim theRequest As HttpWebRequest Try 'Controlla che il file esista theRequest = WebRequest.Create(Me.txtFileName.Text) theResponse = theRequest.GetResponse Catch ex As Exception MessageBox.Show("An error occurred while downloading file. Possibe 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 'Dimensione (in Byte) dello stream di risposta Dim safedelegate As New ChangeTextsSafe(AddressOf ChangeTexts) Me.Invoke(safedelegate, length, 0, 0, 0) 'Invoca la TreadsafeDelegate Dim writeStream As New IO.FileStream(Me.whereToSave, IO.FileMode.Create) 'Per rimpiazzare la proprietà Stream.Position (il webstream non supporta il Seek) Dim nRead As Integer 'Per calcolare la velocità di download Dim speedtimer As New Stopwatch Dim currentspeed As Double = -1 Dim readings As Integer = 0 Do If BackgroundWorker1.CancellationPending Then 'If user abort download 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 'Per migliorare la precisione calcola la velocità di download solo ogni cinque cicli currentspeed = 20480 / (speedtimer.ElapsedMilliseconds / 1000) speedtimer.Reset() readings = 0 End If Loop 'Chiude gli stream 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
Comunque, è necessario usare alcune delegate per accedere agli eventi UI (in quanto si trovano nell’ altro thread, non in quello del backgroundworker).
Attualmente il programma non supporta il download dei file via FTP, ma comunque non penso sia difficile aggiungere questa funzionalità.
Riprendere i download
Per cominciare il download da un punto preciso (ad esempio quando si vuole riprendere un download messo in pausa) è sufficiente aggiungere il seguente codice prima di usare il metodo GetResponse della WebRequest.
Dim theRequest As HttpWebRequest theRequest.AddRange(daDoveVuoiCominciare) '<- aggiungere questo
"daDoveVuoiCominciare" è un valore Integer che rappresente il punto dal quale partire (in byte).
Attenzione! Bisogna anche aprire lo stream di scrittura dove è contenuta la prima parte del file e impostare la posizione alla fine del file:
Dim writeStream As New IO.FileStream(Me.whereToSave, IO.FileMode.Open) writeStream.Position = daDoveVuoiCominciare
Calcolare la velocità di download
Per calcolare la velocità di download questo programma usa un metodo semplice: calcola (usando la classe StopWatch) il tempo per eseguire un ciclo (ovvero 4 kb da scaricare) e ogni cinque cicli (per aumentare la precisione) divide il buffer * 5 (in questa applicazione il buffer è di 4096 bytes, quindi questo valore è di 20480) per il tempo per eseguire un ciclo (in secondi) e si ottiene la velocità in byte/secondo.
currentspeed = 20480 / (speedtimer.ElapsedMilliseconds / 1000)
Però, la classe StopWatch non è molto precisa e quindi la velocità può oscillare di 5-6 kb in più o in meno rispetto a quella reale.
E' tutto! Spero che questo articolo vi sia di aiuto nei vostri futuri programmi.
Molto interessante.
Ciao.
Grazie – molto utile!
Molto buono. L’unico peccato è che fa errore qui:
Me.Invoke(safedelegate, length, nRead, percent, currentspeed)
Messaggio dell’eccezione: (ArgumentOutOfRangeException)
‘-10300’ non è un valore valido per ‘Value’. ‘Value’ deve essere compreso tra ‘minimum’ e ‘maximum’. Nome parametro: Value
Copioni, ho visto gli STESSI codici in un sito inglese, avete solo tradotto in italiano.
Ecco il link del sito: http://www.codeproject.com/Articles/17979/Downloading-Files-in-NET-With-All-Information-Prog
Ciao,
Sono contento che tu frequenti CodeProject 🙂
E no, non siamo copioni; quell’articolo è il mio, solo tradotto da me in inglese. Io sono registrato con nickname “Carmine_XX” su CodeProject 😉
E se cerchi in “about the authors”, in fondo all’articolo di CodeProject, troverai il mio nome ed il link a http://www.thetotalsite.it.