Guida al Visual Basic .NET
Capitolo 81° - Scaricare file dalla rete
Oltre al WebBrowser, ci sono altri tre modi di scaricare file da internet. In questo paragrafo li analizzerò uno per uno. Download sincrono gestito
Il primo e più semplice dei suddetti modi consiste nell'utilizzare una classe messa a disposizione dal Framework, ossia WebClient (del namespace System.Net). Una volta istanziato un oggetto di questo tipo, è possibile richiamare da esso molti metodi diversi per scaricare praticamente qualsiasi cosa. Qui espongo i metodi sincroni:
Ci sono, poi, altri membri che è interessante conoscere:
Alcuni semplici esempi: Dim W As New Net.WebClient 'Scarica l'home page del sito e la salva in C: W.DownloadFile("http://totem.altervista.org/index.php", "C:index.php") Dim S As String 'Scarica il contenuto del file Capitoli.txt e lo salva 'nella stringa S S = W.DownloadString("http://totem.altervista.org/guida/versione3/Capitoli.txt") 'Aggiunge una coppia nome-valore alla query W.QueryString.Add("name", "twaveeditor") 'La prossima richiesta sarà quindi equivalente a: ' http://totem.altervista.org/download/details.php?name=twaveeditor 'Ossia scaricherà la pagina di download di TWave Editor. 'Il contenuto del file verrà salvato in B Dim B() As Byte = W.DownloadData("http://totem.altervista.org/download/details.php")
La pecca di questi metodi è che sono sincroni, ossia bloccano il funzionamento dell'applicazione fino a quando il download non
è terminato. Questo comportamento può rivelarsi utile in certi casi e rendere più maneggevole il codice per scaricare
file di piccole dimensioni, ma è tutt'altro che accettabile per grandi quantità di dati. Download asincrono gestito
Per file molto grandi, invece, ci vengono in aiuto le versioni asincrone dei metodi sopra esposti: sono riconoscibili dal suffisso "Async"
dopo il nome del metodo. Questi eseguono il download in un thread separato, perciò non interferiscono con le normali operazioni
del programma. In compenso, sono un po' più difficili da gestire, ma nulla di particolarmente complicato. Class Form1 'WithEvents permette di gestire gli eventi di W Private WithEvents W As New Net.WebClient() Private Sub btnDownload_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDownload.Click 'Inizia il download asincrono W.DownloadFileAsync(New Uri(txtUrl.Text), txtFile.Text) btnCancel.Enabled = True btnDownload.Enabled = False End Sub Private Sub W_DownloadProgressChanged(ByVal sender As Object, ByVal e As Net.DownloadProgressChangedEventArgs) Handles W.DownloadProgressChanged 'Il parametro e contiene alcune informazioni 'sul progresso del download lblStatus.Text = _ String.Format("Bytes ricevuti: {0} B{3}Dimensione file: {1} B{3}Progresso: {2:N0}%", _ e.BytesReceived, e.TotalBytesToReceive, _ e.ProgressPercentage, Environment.NewLine) End Sub Private Sub W_DownloadFileCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs) Handles W.DownloadFileCompleted 'e.Cancelled vale True se il download è stato annullato. 'e.Error è di tipo Exception e contiene l'eccezione ' generata nel caso si sia verificato un errore. If e.Cancelled Then MessageBox.Show("Il download è stato cancellato!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation) ElseIf e.Error IsNot Nothing Then MessageBox.Show("Si è verificato un errore: " & e.Error.Message, Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Else MessageBox.Show("Download completato con successo!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information) End If btnDownload.Enabled = True btnCancel.Enabled = False End Sub Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click 'Il metodo CancelAsync cancella il download asincrono W.CancelAsync() btnDownload.Enabled = True btnCancel.Enabled = False End Sub End Class
Download sincrono/asincrono non gestito
Come ho illustrato nei paragrafi precedenti, WebClient si occupa di eseguire molte istruzioni riguardo al download:
connettersi al server indicato, creare una richiesta valida secondo il protocollo usato (HTTP o FTP o altri), inoltrare la richiesta,
aspettare una risposta, leggere dallo stream di rete i dati forniti dal server e copiarli nel file indicato, quindi chiudere la connessione.
Insomma, non lascia nulla al controllo del programmatore. Con il prossimo metodo che andrò ad introdurre, potremmo manipolare alcuni
di questi passaggi a nostro piacimento. Public Sub DownloadFile(ByVal Address As String, ByVal Path As String) 'Crea una richiesta http per l'indirizzo Address. 'Address può anche contenere una query string Dim Request As Net.HttpWebRequest = Net.HttpWebRequest.Create(Address) 'Invia la richieste a ottiene la risposta Dim Response As Net.HttpWebResponse = Request.GetResponse() 'Ottiene da Response uno stream di rete dal quale si 'potrà leggere il file richiesto. Dim Reader As IO.Stream = Response.GetResponseStream() 'Crea un nuovo file in locale Dim Writer As New IO.FileStream(Path, IO.FileMode.Create) 'Un buffer di byte che contiene i blocchi letti 'dallo stream. La lettura a blocchi è più 'conveniente che trasferire in massa tutto il contenuto, 'poiché altrimenti si dovrebbe usare un buffer 'gigantesco (almeno le dimensioni del file) Dim Buffer(8127) As Byte Dim BytesRead As Int32 'La funzione Read, vi ricordo, restituisce come risultato 'il numero di bytes effettivamente letti dallo stream BytesRead = Reader.Read(Buffer, 0, Buffer.Length) Do While BytesRead > 0 Writer.Write(Buffer, 0, BytesRead) BytesRead = Reader.Read(Buffer, 0, Buffer.Length) Loop Reader.Close() Writer.Close() Response = Nothing Request = Nothing End Sub
Prima di procedere, vorrei fare alcuni chiarimenti sullo stream di rete. Esso rappresenta un flusso di dati che proviene dal server a
cui si è inviata la richiesta, ed è un flusso a senso unico, perciò non supporta operazioni di ricerca (invocando il
metodo Seek o modificando la proprietà Position otterrete degli errori). Non è neppure possibile saperne la dimensione
complessiva, poiché anche la proprietà Length genera eccezioni. E, infine, non è possibile scrivervi sopra. Esiste
un modo per sapere le dimensioni dei dati, ossia richiamare la proprietà Reponse.ContentLength, che, tuttavia, potrebbe contenere
valori privi di senso (ad esempio -1). Questo succede perchè essa si limita ad esporre il valore di un header posto nella risposta
http: tuttavia, il server non è obbligato ad inserire questo header, e se non lo fa, non c'è modo di leggerlo. '... 'Indica al server che vogliamo iniziare la lettura 'dall'offset n Request.AddRange(n) 'oppure 'Indica al server che vogliamo iniziare la lettura dalla 'posizione n, ma solo fino alla posizione q Request.AddRange(n, q) Non serve eseguire altre operazioni particolari per la lettura. Lo stream ottenuto consentirà di leggere esattamente ciò che si è richiesto come se fosse un unico flusso di dati.
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|