Introduzione
Fin dalla prima versione del Framework chiunque avesse voluto creare un client FTP o comunque interagire con il protocolli, doveva costruirsi una propria classe (oppure utilizzare i componenti già pronti) … e l'
FTP non è conosciuto come il protocollo più semplice del web.
Ora con l'avvento del
Framework 2.0 mamma Microsoft ha pensato anche a quei poveri sviluppatori che hanno perso nottate a colpi di socket, introducendo due nuove classi
FtpWebRequest e
FtpWebResponse.
N.B. Per l'articolo sono stati utlizzati esempi in
C#, il progetto allegato all'articolo è anche in versione
VB.NET.
WebRequest & WebResponse
Per implementare le nuove classi Microsoft ha utilizzato delle classi già presenti nelle versioni precedenti, infatti le classi FtpWebRequest / FtpWebResponse sono derivate rispettivamente da
WebRequest / WebResponse. Questa dipendenza comporta:
1. Modello programmazione URI Based: ogni comando effettuato su una cartella o un file deve essere identificato tramite
URI (Uniform Resource Identifier), come se fosse una qualsiasi pagina web, ad esempio
ftp://mioserver/miacartella/miofile.txt2. Richieste indipendenti: Ogni richiesta è indipendente dalle altre, quindi ad ogni comando è necessario creare una nuova richiesta e fornire delle credenziali per l'accesso.
3. Standard Stream: In upload e in download è possibile utilizzare classi già implementate nel .NET framework come
StreamReader e StreamWriter.
4. Opzioni avanzate: Sono state implementate alcune funzionalità avanzate come la possibilità di specificare il
proxy o utilizzare l'
FTP over SSL.
Prima connessione
Incominciamo a vedere un po' di codice per capire come funziona in pratica la nuova classe. Prima di tutto bisogna creare una nuova richiesta:
FtpWebRequest _FtpRequest = (FtpWebRequest) WebRequest.Create("ftp://mioserver/”);
A questo punto è stata creata una nuova richiesta FTP, alla quale assoceremo delle
credenziali per l'autenticazione al server:
_FtpRequest.Credentials = new NetworkCredential(_UserName, _Password);
Ora dovremo allegare alla nostra richiesta il comando che vogliamo eseguire, ad esempio l'elenco dei file e delle cartelle:
_FtpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
L'elemento
ListDirectoryDetails fa parte della classe
WebRequestMethods.Ftp, che al suo interno contiene tutti i comandi FTP implementati, di seguito l’elenco:
- AppendFile (Comando
FTPAPPE, append a un file esistente sul server)
- DeleteFile (Comando
FTPDELE, elimina un file sul server)
- DownloadFile (Comando
FTPRETR, download di un file sul server)
- GetDateTimestamp (Data/ora server)
- GetFileSize (Comando
FTPSIZE, dimensione file)
- ListDirectory (Comando
FTPNLIST, lista breve di file e directory)
- ListDirectoryDetails (Comando
FTPLIST, lista dettagliata di file e directory)
- MakeDirectory (Comando
FTPMKD, crea cartella sul server)
- PrintWorkingDirectory (Comando
FTPPWD, ritorna il nome della directory di lavoro)
- RemoveDirectory (Comando
FTPRMD, elimina la directory)
- Rename (Comando
FTPRENAME, rinomina)
- UploadFile (Comando
FTPSTOR, upload di un file)
- UploadFileWithUniqueName (Comando
FTPSTOU, carica un file nome univoco sul server)
Allegata la richiesta, eseguiamo la
GetResponse, che ritornerà uno
Stream con la risposta del server:
FtpWebResponse _FtpResponse = (FtpWebResponse) _FtpRequest.GetResponse();
Per poter "leggere" la risposta del server dobbiamo implementare un oggetto
StreamReader, specificando anche il tipo di encoding (in questo caso ASCII):
StreamReader sr = new StreamReader(_FtpResponse.GetResponseStream(), System.Text.Encoding.ASCII);
Ed infine stampiamo la stringa con la risposta del server
Console. WriteLine(sr.ReadToEnd());
Eccezioni & Opzioni
Nel caso si scateni un'ecezione è possibile intercettare l'errore e visualizzare al posto di un errore standard 500, troppo generico, con un messaggio che interpreta lo status code inviato dal server, utilizzando la proprietà
StatusDescription, in questo modo:
catch (WebException e)
{
Console.WriteLine(((FtpWebResponse)e.Response).StatusDescription);
}
Inoltre come illustrato nell'introduzione è possibile utilizzare alcune opzioni avanzate, come proxy e SSL:
//Utilizzo Proxy
_FtpRequest.Proxy = new WebProxy("http://myproxy");
//FTP over SSL
_FtpRequest.EnableSSL = true;
Download file via FTP
Dopo aver visualizzato l'elenco dei file e delle cartelle, vediamo come scaricare un file. Quindi dobbiamo creare una nuova richiesta
FTP, elencare le nostre credenziali e selezionare come comando
WebRequestMethods.Ftp.DownloadFile:
FtpWebRequest _FtpRequest = (FtpWebRequest)WebRequest.Create("ftp://mioserver/file.txt”);
_FtpRequest.Credentials = new NetworkCredential(_UserName, _Password);
_FtpRequest.Method = WebRequestMethods.Ftp.DownloadFile;
FtpWebResponse _FtpResponse = (FtpWebResponse)_FtpRequest.GetResponse();
Ora creo un oggetto
FileStream per poter creare il file in locale:
FileStream _FileStream = new FileStream(“file.txt”, FileMode.Create, FileAccess.Write);
Dopo di che creo l’oggetto
FtpWebResponse per poter ricavare lo stream del server e passarlo al
FileStream e creare il file:
//ricavo lo stream FTP
_FtpResponse = (FtpWebResponse)_FtpRequest.GetResponse();
//converto lo stram FTP in un oggetto standard Stream
Stream _ResponseStream = _FtpResponse.GetResponseStream();
//Creo l’array di byte
byte[] buffer = new byte[1024];
//Carico la prima serie di byte nell’array
int bytesRead = _ResponseStream.Read(buffer, 0, 1024);
//ciclo fino alla fine dello stream di byte
while (bytesRead != 0)
{
//Scrivo l’array di byte
_FileStream.Write(buffer, 0, bytesRead);
bytesRead = _ResponseStream.Read(buffer, 0, 1024);
}
//Chiudo gli oggetti
_FileStream.Close();
_ResponseStream.Close();
Una feature che si può implementare è il
Resume del download del file, ad esempio mandare in pausa il download e poi riprenderlo in un secondo tempo, utilizzando la proprietà
ContentOffset, che indica da quale byte riprendere la lettura dello stream:
//creo un oggetto fileInfo
FileInfo _File = new FileInfo(“file.txt”);
// Ricavo la lunghezza del mio file in locale, e la imposto come offset,
// in modo che la scrittura parta dalla fine del file.
_FtpRequest.ContentOffset = _File.Length;
//Apro lo Stream in Append
_FileStream = new FileStream(_File.FullName, FileMode.Append, FileAccess.Write);
Upload di un file via FTP
Altra operazione irrinunciabile per un
client FTP che si voglia definire tale è l'upload di un file. La procedura è molto simile al download, a parte il comando FTP che sarà
WebRequestMethods.Ftp.UploadFile, e l'uso dello stream che sarà invertito. Quindi dopo aver ricreato una nuova richiesta con le credenziali annesse, dobbiamo specificare il nuovo comando:
FtpWebRequest _FtpRequest.Method = WebRequestMethods.Ftp.UploadFile;
Dopo di che andremo a creare un array di byte lungo quanto la grandezza del nostro file, che riempiremo con un
FileStream:
//creo array di byte
byte[] _fileContents = new byte[_File.Length];
//apro il fileStream
FileStream fr = _File.OpenRead();
//Riempio l’array
fr.Read(_fileContents, 0, Convert.ToInt32(_File.Length));
//chiudo lo stream
fr.Close();
Ottenuto il nostro array non ci resterà altro da fare che creare uno
stream FTP e andare a scrivere il contenuto del nostro array sullo stream:
//ricavo lo stream FTP
Stream writer = _FtpRequest.GetRequestStream();
//Vado a scrivere il contenuto del mio array
writer.Write(_fileContents, 0, _fileContents.Length);
//Chiudo lo stream
writer.Close();
Eliminare un file
Visto che l'eliminazione di un file non richiede l'utilizzo di
Stream, ma esclusivamente lesecuzione di un comando, non dovremmo far altro che eseguire una
FtpWebResponse, in questo modo:
//Creo la richiesta
FtpWebRequest _FtpRequest = (FtpWebRequest)WebRequest.Create("ftp://mioserver/file.txt”);
//Passo le credenziali
_FtpRequest.Credentials = new NetworkCredential(_UserName, _Password);
//Imposto il comando
_FtpRequest.Method = WebRequestMethods.Ftp.DeleteFile;
A questo punto ricavo lo
stream FTP:
_FtpResponse = (FtpWebResponse)_FtpRequest.GetResponse();
Stream _ResponseStream = _FtpResponse.GetResponseStream();
_ResponseStream.Close();
Conclusioni
Ora non avrete più scuse se il vostro
Project Manager vi chiederà di creare un client FTP, con le nuove classi del
Framework 2.0 non avrete più limiti. Come abbiamo visto con poche righe di codice è possibile creare un client FTP, unico neo, a mio modesto parere, il dover specificare per ogni comando
l'URI e le credenziali, ma a caval donato non si guarda in bocca ;)