Il presente articolo è stato tradotto automaticamente.

Windows Phone

Aggiunta del supporto FTP in Windows Phone 8

Uday Gupta

Scaricare il codice di esempio

FTP è uno dei protocolli più ampiamente usati per la condivisione di file. È usato a portata di mano i file al client, per distribuire i file di installazione per i consumatori, per fornire l'accesso a zone altrimenti inaccessibili del file system per motivi di sicurezza in ambienti aziendali e per una miriade di altri scenari. Con tutta l'enfasi sul cloud, potrebbe sembrare controintuitivo oggi per continuare a fare affidamento su FTP. Ma se volete avere il controllo diretto dei file, fornendo facile accesso ad essi, FTP può essere ancora la strada da percorrere.

L'aggiunta del supporto presa in Windows Phone abilitato i dispositivi comunicare con una moltitudine di servizi diversi, rendendo gli utenti sentono più collegato e disponibile che mai. Tuttavia, in Windows Phone (così come Windows 8), può essere fino allo sviluppatore di fornire la propria implementazione di un servizio. Una caratteristica che manca ancora in Windows Phone è il supporto per FTP — non ci sono nessun dirette API disponibili per sfruttare i servizi FTP in un'applicazione Windows Store. Dal punto di vista aziendale, questo lo rende piuttosto difficile per i dipendenti di accedere ai file sul loro telefoni.

Questo articolo è di fornire supporto per FTP su Windows Phone 8 creando una libreria FTP e una semplice applicazione client FTP che verrà eseguito su un dispositivo Windows Phone. FTP è un protocollo ben consolidato, ben documentato (vedere RFC 959 a bit.ly/fB5ezA). RFC 959 descrive la specifica, la funzionalità e architettura e i comandi con le loro risposte. Non ho intenzione di descrivere ogni caratteristica e comando di FTP, ma spero di fornire un inizio testa per aiutarti a creare la propria implementazione FTP. Si noti che la funzione che ho intenzione di discutere non è un'implementazione di Microsoft e non possa necessariamente essere approvata.

Canali FTP

FTP è costituito da due canali, un canale di comando e un canale di dati. Il canale di comando viene creato quando un client si connette a un server FTP, e viene utilizzato per trasmettere tutti i comandi e le risposte da e verso server FTP. Il canale di comando rimane aperto fino a quando il client si disconnette, avviene un connessione-idle timeout o si verifica un errore nel canale FTP server o comando.

Il canale di dati è utilizzato per trasmettere dati da e verso server FTP. Questo canale è di carattere temporaneo, una volta completato il trasferimento dei dati, il canale è scollegato. Per ogni trasmissione di dati, è stabilito un nuovo canale di dati.

Una connessione FTP può essere attivo o passivo. In modalità attiva, il client FTP invia le informazioni del canale dati (il numero di porta dati) a cui verrà inviato il trasferimento di file. In modalità passiva, il client richiede al server di creare un canale di dati sulla sua fine e fornire le informazioni di indirizzo e la porta di presa, così il cliente può connettersi a quel canale dati e iniziare l'operazione di trasferimento file.

Modalità passiva è utile quando il client FTP non vuole gestire dati porti o canali di dati sulla sua fine. Mentre lo sviluppo di client FTP per Windows Phone, potrai passare alla modalità passiva. Che è, prima di iniziare un'operazione di trasferimento file, chiederò il server FTP per creare e aprire un canale di dati e di inviarmi le informazioni sull'endpoint presa così che quando mi connetto, trasferimento dati inizia. Verrà illustrato come utilizzare la modalità passiva più avanti nell'articolo.

Guida introduttiva

Questo articolo non coprirà tutti i comandi menzionati nella RFC 959. Mi concentrerò invece i comandi di base necessari per costruire un client FTP funzionante, minimal, inclusi quelli coinvolti nella connessione-scollegare la procedura di autenticazione, navigazione nel file system e caricare e scaricare file.

Per iniziare, creerò una soluzione Visual Studio che conterrà due progetti, un progetto libreria di classi di Windows Phone per la libreria FTP e un progetto Windows Phone App per contenere il codice per la UX e utilizzando la libreria FTP.

Per creare la soluzione, aprire Visual Studio, creare un progetto libreria di classi di Windows Phone e denominarlo WinPhoneFtp.FtpService.

Ora aggiungere un progetto Windows Phone App e denominarlo WinPhone­-Ftp.UserExperience. Questo sarà con­tain l'interfaccia utente per l'applicazione di prova.

WinPhoneFtp.FtpService è la libreria client FTP che farà riferimento al progetto WinPhoneFtp.UserExperience UX per poter utilizzare servizi FTP. La libreria FTP utilizza un modello basato su eventi, async/attendono. Ogni operazione verrà chiamato in modo asincrono in un'attività in background e, al termine, verrà generato un evento per fornire una notifica nell'interfaccia utente. La libreria client FTP contiene vari metodi asincroni per ogni operazione di FTP (comandi supportati) e ciascun metodo asincrono viene mappata a un determinato evento che viene generato quando il metodo asincrono completa. Figura 1 raffigura il mapping di metodi asincroni ed eventi.

Figura 1 metodi asincroni ed eventi associati

Metodo (parametri) Eventi associati
Costruttore (System. String IPAddress, System.Windows.Threading.Dispatcher UIDispatcher) Nessun evento associato
ConnectAsync FtpConnected
DisconnectAsync FtpDisconnected

AuthenticateAsync

AuthenticateAsync (System. String Username, Password di System. String)

Successo - FtpAuthenticationSucceeded

Fallimento - FtpAuthenticationFailed

GetPresentWorkingDirectoryAsync FtpDirectoryListed
ChangeWorkingDirectoryAsync

Successo - FtpDirectoryChangedSucceeded

Fallimento - FtpDirectoryChangedFailed

GetDirectoryListingAsync FtpDirectoryListed
UploadFileAsync (System.IO.Stream LocalFileStream, String RemoteFilename)

Successo - FtpFileUploadSucceeded

Fallimento - FtpFileUploadFailed

Progresso - FtpFileTransferProgressed

DownloadFileAsync (System.IO.Stream LocalFileStream, String RemoteFilename)

Successo - FtpFileDownloadSucceeded

Fallimento - FtpFileDownloadFailed

Progresso - FtpFileTransferProgressed

Interfaccia utente dell'applicazione è costituito da due caselle di testo e due pulsanti. Serviranno per accettare l'indirizzo IP del server FTP e collegare ad esso una textbox e button. Altri controlli textbox e pulsante accetterà i comandi FTP da un utente e li inviano al server FTP.

Socket TCP Wrapper

Prima di avviare il client FTP, ho creato un wrapper denominato TcpClientSocket per la classe di Windows.Networking.Sockets.StreamSocket (vedere bit.ly/15fmqhK). Questo wrapper fornirà alcune notifiche di eventi basate su varie operazioni di presa. Le notifiche che mi interessano sono SocketConnected, DataReceived, ErrorOccured e SocketClosed. Quando si collega l'oggetto StreamSocket all'endpoint remoto, verrà generato l'evento SocketConnected. Analogamente, sulla ricezione dati sopra lo zoccolo, i dati­ricevuto l'evento verrà generato, insieme con il buffer contenente dati come gli argomenti dell'evento. Se si verifica un errore durante l'operazione di socket, è l'evento di ErrorOccured che fa la notifica. E, infine, quando la presa è chiuso (da un host sia locale o remoto), l'evento SocketClosed viene generato, con il motivo della chiusura nel suo argomento dell'evento. Io non entrerò nei dettagli sull'implementazione del wrapper di presa, ma è possibile scaricare il codice sorgente per questo articolo e vedere la sua attuazione (archive.msdn.microsoft.com/mag201309WPFTP).

FTP collegare e scollegare

Quando il client FTP in primo luogo si connette al server FTP, stabilisce un canale di comando con il server su cui comandi e le loro risposte sono condivisi. In genere, FTP utilizza la porta 21, ma per motivi di sicurezza o da altri fattori, può essere configurato un numero di porta diverso. Prima che un utente può iniziare la condivisione di file, ha bisogno di autenticarsi al server, in genere con un username e una password. Se l'autenticazione ha esito positivo, il server risponderà (nel mio caso): 220 Microsoft FTP Service.

Prima della connessione al server FTP, creerò un oggetto denominato FtpClient basato sulla mia classe di libreria client FTP, insieme con i gestori di eventi per tutti gli eventi descritti in precedenza:

FtpClient ftpClient = null;
async private void btnLogin_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ftpClient = new FtpClient(txtIp.Text, this.Dispatcher);
  // Add event-handlers to various events of ftpClient object
  await ftpClient.ConnectAsync();
}

Una volta che l'oggetto viene creato, e vengono aggiunti vari gestori di eventi client FTP, chiamerò il metodo ConnectAsync dell'oggetto FtpClient. Ecco come funziona ConnectAsync:

public async Task ConnectAsync()
{
  if (!IsConnected)
  {
    logger.AddLog("FTP Command Channel Initailized");
    await FtpCommandSocket.PrepareSocket();
  }
}

ConnectAsync si connetterà al server FTP come un'operazione di socket-collegare. Quando il client FTP si connette correttamente al server FTP, l'evento FtpConnected viene generato per notificare al client che è collegato al server FTP:

async void ftpClient_FtpConnected(object sender, EventArgs e)
{
  // Handle the FtpConnected event to show some prompt to
  // user or call AuthenticateAsync to authenticate user
  // automatically once connected
}

Figura 2 dimostra che l'app sarà simile quando un utente si è connesso correttamente al server FTP.

Connecting and Authenticating to the FTP Server
Figura 2 collegamento e autenticazione al Server FTP

Si noti che quando si collega ad altri server FTP, il testo che vedete possa essere del tutto diverso.

Ci sono tre modi per diventare scollegato da un server FTP:

  • L'utente invia un comando QUIT al server deliberatamente chiudere la connessione.
  • Dopo essere stato inattivo per un tempo specificato, il server FTP chiude la connessione.
  • Su una o entrambe le estremità, può verificarsi un errore di socket o qualche errore interno.

Per disconnettere deliberatamente tramite il comando QUIT, emetto QUIT nella casella di testo del comando e il gestore di eventi del pulsante Invia comando rubinetto chiama il metodo DisconnectAsync:

async private void btnFtp_Tap(object sender, 
    System.Windows.Input.GestureEventArgs e)
{
  ...
if (txtCmd.Text.Equals("QUIT"))
  {
    logger.Logs.Clear();
    await ftpClient.DisconnectAsync();
    return;
  }
  ...
}

Questo è come DisconnectAsync invia il comando al server FTP:

public async Task DisconnectAsync()
{
  ftpCommand = FtpCommand.Logout;
  await FtpCommandSocket.SendData("QUIT\r\n");
}

Una volta chiusa la connessione, viene generato l'evento FtpDisconnected, così che il cliente può gestire l'evento di disconnessione e con garbo di rilasciare le risorse, con il risultato mostrato Figura 3:

void ftpClient_FtpDisconnected(object sender, 
    FtpDisconnectedEventArgs e)
{
  // Handle FtpDisconnected event to show some prompt
  // or message to user or release
}

Disconnecting from the FTP Server
Figura 3 disconnessione dal Server FTP

In uno degli scenari menzionati, il client FTP è disconnesso dal server FTP e anche si interromperà qualsiasi in corso operazioni di trasferimento file FTP.

Autenticazione FTP

Prima di iniziare qualsiasi operazione di file FTP, l'utente deve autenticarsi al server FTP. Ci sono due tipi di utenti: anonimi e non anonimi (quelli con le credenziali). Quando il server FTP è accessibile da chiunque, gli utenti vengono autenticati come anonimo. Per loro, il nome utente è "anonimo" e la password può essere qualsiasi testo, formattato come un indirizzo di posta elettronica. Per FTP non anonimo, gli utenti devono fornire credenziali valide. Prima di attuare qualsiasi schema di autenticazione, è necessario sapere quale tipo di autenticazione è abilitata sul server.

L'autenticazione avviene tramite due comandi FTP, USER e PASS. Il comando USER viene utilizzato per inviare l'identità dell'utente, ovvero il nome utente. Il comando PASS viene utilizzato per fornire la password. Anche se è buona pratica per fornire l'indirizzo di posta elettronica dell'utente, qualsiasi testo formattato come un indirizzo di posta elettronica funzionerà:

[FTP Client]: USER anonymous
[FTP Server:] 331 Anonymous access allowed, send identity (e-mail name) as password
[FTP Client]: PASS m@m.com
[FTP Server]: 230 User logged in

Il metodo AuthenticateAsync ha due overload. Il primo overload autenticherà l'utente con le credenziali predefinite, che sono generalmente utilizzati per l'autenticazione anonima. Il secondo overload richiede un nome utente e password per l'autenticazione al server FTP.

Per autenticare l'utente automaticamente, come utente anonimo, chiamo AuthenticateAsync quando viene ricevuto l'evento FtpConnected:

async void ftpClient_FtpConnected(object sender, 
    EventArgs e)
{
  await (sender as FtpClient).AuthenticateAsync();
}

AuthenticateAsync chiama internamente l'altro overload con credenziali predefinite:

public async Task AuthenticateAsync()
{
  await AuthenticateAsync("anonymous", m@m.com);
}

I problemi di sovraccarico AuthenticateAsync il comando dell'utente al server FTP con lo username ricevuto dal parametro:

public async Task AuthenticateAsync(String Username, 
    String Password)
{
  ftpCommand = FtpCommand.Username;
  this.Username = Username;
  this.Password = Password;
  logger.AddLog(String.Format("FTPClient -> USER {0}\r\n", 
    Username));
  await FtpCommandSocket.SendData(String.Format("USER {0}\r\n", 
    Username));
}

Quando viene ricevuta la risposta al comando dell'utente, il comando PASS viene emesso insieme con la password ricevuta dal parametro (vedere Figura 4).

Figura 4 inviare la Password con PASS, dopo il comando dell'utente

async void FtpClientSocket_DataReceived(object sender, 
    DataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  logger.AddLog(String.Format("FTPServer -> {0}", Response));
    switch (ftpPassiveOperation)
    {
      ...
case FtpPassiveOperation.None:
        switch (ftpCommand)
        {
          case FtpCommand.Username:
          if (Response.StartsWith("501"))
          {
            IsBusy = false;
            RaiseFtpAuthenticationFailedEvent();
            break;
          }
          this.ftpCommand = FtpCommand.Password;
          logger.AddLog(String.Format(
            "FTPClient -> PASS {0}\r\n", this.Password));
          await FtpCommandSocket.SendData(
            String.Format("PASS {0}\r\n", this.Password));
          break;
          ...
}
    }
    ...
}

Se l'autenticazione ha esito positivo, viene generato l'evento di FtpAuthenticationSucceeded; in caso contrario, viene generato l'evento FtpAuthenticationFailed:

void ftpClient_FtpAuthenticationFailed(object sender, 
    EventArgs e)
{
  logger.AddLog("Authentication failed");
}
void ftpClient_FtpAuthenticationSucceeded(object sender, 
    EventArgs e)
{
  logger.AddLog("Authentication succeeded");
}

Esplorazione directory

FTP fornisce supporto per l'esplorazione delle directory, ma questo dipende da come è scritto il client FTP per visualizzare le informazioni sulla directory. Se il client FTP è una GUI, le informazioni possono essere visualizzate come un albero di directory. Un client console, al contrario, può solo visualizzare la directory listing sullo schermo.

Perché non non c'è nessun meccanismo fornito per farvi sapere quale directory sei in, FTP fornisce un comando per mostrare l'attuale directory di lavoro (directory corrente). Invio il comando PWD al server FTP tramite il canale di comando ti dà l'intero percorso, dalla radice della directory corrente. Quando un utente viene autenticato, atterra in directory principale o la directory configurata per lui nella configurazione del server FTP.

È possibile eseguire il comando PWD chiamando GetPresentWorkingDirectoryAsync. Per ottenere la directory corrente, io rilascio PWD nella casella di testo comando e chiamare GetPresentWorkingDirectoryAsync nel comando Invia evento del pulsante tap:

async private void btnFtp_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ...
if (txtCmd.Text.Equals("PWD"))
  {
    logger.Logs.Clear();
    await ftpClient.GetPresentWorkingDirectoryAsync();
    return;
  }
  ...
}

Internamente, GetPresentWorkingDirectoryAsync invia PWD al server FTP sopra lo zoccolo:

public async Task GetPresentWorkingDirectoryAsync()
{
  if (!IsBusy)
  {
    ftpCommand = FtpCommand.PresentWorkingDirectory;
    logger.AddLog("FTPClient -> PWD\r\n");
    await FtpCommandSocket.SendData("PWD\r\n");
  }
}

Quando questa procedura viene eseguita correttamente, il server FTP invia la risposta con il percorso della cartella di lavoro presenti, il client FTP è notificato e viene generato l'evento FtpPresentWorkingDirectoryReceived. Utilizzando gli argomenti dell'evento, il client può ottenere le informazioni sul percorso della directory di lavoro presente (come mostrato Figura 5):

void ftpClient_FtpPresentWorkingDirectoryReceived(object sender,
  FtpPresentWorkingDirectoryEventArgs e)
{
  // Handle PresentWorkingDirectoryReceived event to show some
  // prompt or message to user
}

Getting the Present Working Directory from the FTP Server
Figura 5 ottenendo il presente lavoro Directory dal Server FTP

Per modificare alcune altre directory, il client FTP può utilizzare il comando Directory cambiare lavoro (CWD). Si noti che CWD funziona solo per cambiare una sottodirectory nella directory di lavoro corrente o alla directory padre. Quando l'utente si trova nella directory radice, egli non può spostarsi all'indietro e CWD genererà un errore in risposta.

Per cambiare la directory di lavoro, ho il problema del comando CWD nella casella di testo comando e nel comando Invia evento del pulsante tap possibile chiamare il metodo ChangeWorkingDirectoryAsync:

async private void btnFtp_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ...
if (txtCmd.Text.StartsWith("CWD"))
  {
    logger.Logs.Clear();
    await ftpClient.ChangeWorkingDirectoryAsync(
      txtCmd.Text.Split(new char[] { ' ' },
      StringSplitOptions.RemoveEmptyEntries)[1]);
    return;
  }
  ...
}

Internamente, ChangeWorkingDirectoryAsync emette il comando CWD al server FTP:

public async Task ChangeWorkingDirectoryAsync(String RemoteDirectory)
{
  if (!IsBusy)
  {
    this.RemoteDirectory = RemoteDirectory;
    ftpCommand = FtpCommand.ChangeWorkingDirectory;
    logger.AddLog(String.Format("FTPClient -> CWD {0}\r\n", 
        RemoteDirectory));
    await FtpCommandSocket.SendData(String.Format("CWD {0}\r\n", 
        RemoteDirectory));
  }
}

Se l'utente desidera spostarsi all'indietro, può inviare doppi puntini ".." come parametro a questa procedura. Quando il cambiamento è successo, il cliente è informato e viene generato l'evento FtpDirectoryChangedSucceeded, che può essere visto in Figura 6. Se CWD fallisce e manda un messaggio di errore in risposta, il client riceve una notifica di quel fallimento e viene generato l'evento FtpDirectoryChangedFailed:

void ftpClient_FtpDirectoryChangedSucceded(object sender,
  FtpDirectoryChangedEventArgs e)
{
  // Handle DirectoryChangedSucceeded event to show
  // some prompt or message to user
}
void ftpClient_FtpDirectoryChangedFailed(object sender,
  FtpDirectoryChangedEventArgs e)
{
  // Handle DirectoryChangedFailed event to show
  // some prompt or message to user
}

Changing Working Directory on the FTP Server
Figura 6 cambiare Directory di lavoro sul Server FTP

Aggiunta del supporto per comandi Passive

Ora è il momento di fornire supporto per comandi passive, perché voglio che il server FTP per gestire tutte le connessioni dati per trasferire dati da e verso il server. In modalità passiva, qualsiasi comando che per il trasferimento dati deve utilizzare una connessione dati avviata dal server FTP. Il server FTP crea una connessione dati e inviare le informazioni di presa per la connessione dati quindi il client può connettersi ad esso ed effettuare alcune operazioni di trasferimento dei dati.

Modalità passiva è di carattere temporaneo. Prima di inviare un comando al server FTP che inviare o ricevere dati in risposta, il client deve dire al server per accedere alla modalità passiva — e deve farlo per ogni comando inviato. In poche parole, per ogni trasferimento di dati, due comandi sono sempre sparati — passivo e quindi il comando stesso.

Il server FTP è detto per preparare per la modalità passiva tramite il comando PASV. In risposta, invia il numero di porta e indirizzo IP della connessione dati in un formato codificato. La risposta viene decodificata e il client si prepara poi la connessione dati con il server FTP utilizzando il numero di porta e indirizzo IP decodificato. Una volta completata l'operazione di dati, la connessione dati è chiuso e dissolto, così che non può essere utilizzato nuovamente. Questo accade ogni volta che il comando PASV viene inviato al server FTP.

Decodifica la risposta del comando PASV la risposta al comando PASV assomiglia a questo:

227 Entering Passive Mode (192,168,33,238,255,167)

La risposta ha dati canale informazioni in esso contenute. Basato su questa risposta, devo formulare l'indirizzo del socket — la IP indirizzo e dati porta FTP server utilizza per l'operazione di trasferimento dei dati. Ecco i passi per calcolare l'indirizzo IP e la porta, come mostrato Figura 7:

  • I primi gruppi di quattro interi formano l'indirizzo IPV4 del server FTP; cioè, 192.168.33.238.
  • I restanti valori integer comprendono la porta dati. Si-spostamento a sinistra l'intero quinto gruppo con 8 e poi eseguire operazione Bitwise OR con il sesto gruppo intero. Il valore finale ti dà il numero di porta dove il canale di dati sarà disponibile.

Passive Command Request and Reply
Risposta e richiesta di comando passivo figura 7

Figura 8 Mostra il codice analizza la risposta ed estrae l'IP indirizzo e numero di porta dell'endpoint del canale dati. Il metodo PrepareDataChannelAsync riceve le prime risposta FTP per il comando PASV. A volte la stringa di risposta può essere modificata sul server FTP di includere alcuni altri parametri, ma essenzialmente la risposta invia solo l'indirizzo IP e porta numero.

Figura 8 l'analisi dell'Endpoint canale dati

private async Task PrepareDataChannelAsync(String ChannelInfo)
{
  ChannelInfo = ChannelInfo.Remove(0, "227 Entering Passive Mode".Length);
  // Configure the IP Address
  String[] Splits = ChannelInfo.Substring(ChannelInfo.IndexOf("(") + 1,
    ChannelInfo.Length - ChannelInfo.IndexOf("(") - 5).Split(
      new char[] { ',', ' ', },
  StringSplitOptions.RemoveEmptyEntries);
  String Ipaddr = String.Join(".", Splits, 0, 4);
  // Calculate the Data Port
  Int32 port = Convert.ToInt32(Splits[4]);
  port = ((port << 8) | Convert.ToInt32(Splits[5]));
  logger.AddLog(String.Format(
    "FTP Data Channel IPAddress: {0}, Port: {1}", Ipaddr, port));
  // Create data channel here with extracted IP Address and Port number
  logger.AddLog("FTP Data Channel connected");
}

Ulteriori comandi: Elenco, tipo, STOR e RETR

Elenca il contenuto della Directory per elencare il contenuto della directory di lavoro corrente, inviare il comando lista sopra il canale di comando al server FTP. Tuttavia, il server FTP invierà la risposta a questo comando sopra il canale di dati, così devo prima mandare il comando PASV al fine di creare la connessione di dati su cui verrà inviato risposta del comando elenco. Il formato di elenco directory sarà basato sul sistema operativo su cui è installato il server FTP. Cioè, se il sistema operativo del server FTP è Windows, l'elenco di directory sarà ricevuto in formato elenco directory Windows, e se il sistema operativo è basato su Unix, l'elenco di directory sarà ricevuto in formato Unix.

Per elencare il contenuto della directory della cartella di lavoro presente, emetto il comando elenco nella casella di testo comando e nel comando Invia evento del pulsante tap, chiamare il metodo GetDirectoryListingAsync:

async private void btnFtp_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ...
if (txtCmd.Text.Equals("LIST"))
  {
    logger.Logs.Clear();
    await ftpClient.GetDirectoryListingAsync();
    return;
  }
  ...
}

Internamente, GetDirectoryListingAsync invia il comando PASV al server FTP sopra lo zoccolo:

public async Task GetDirectoryListingAsync()
{
  if (!IsBusy)
  {
    fileListingData = null;
    IsBusy = true;
    ftpCommand = FtpCommand.Passive;
    logger.AddLog("FTPClient -> PASV\r\n");
    await FtpCommandSocket.SendData("PASV\r\n");
  }
}

Dopo aver ricevute le informazioni di endpoint per il comando PASV, creata la connessione dati e lista viene emesso il comando poi al server FTP, come mostrato Figura 9.

Figura 9 il comando elenco nell'evento DataReceived di presa di elaborazione

async void FtpClientSocket_DataReceived(object sender, D
    ataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  ...
IsBusy = true;
  DataReader dataReader = new DataReader(
    FtpDataChannel.InputStream);
  dataReader.InputStreamOptions = InputStreamOptions.Partial;
  fileListingData = new List<byte>();
  while (!(await dataReader.LoadAsync(1024)).Equals(0))
  {
    fileListingData.AddRange(dataReader.DetachBuffer().ToArray());
  }
  dataReader.Dispose();
  dataReader = null;
  FtpDataChannel.Dispose();
  FtpDataChannel = null;
  String listingData = System.Text.Encoding.UTF8.GetString(
    fileListingData.ToArray(), 0, fileListingData.ToArray().Length);
  String[] listings = listingData.Split(
    new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
  List<String> Filenames = new List<String>();
  List<String> Directories = new List<String>();
  foreach (String listing in listings)
  {
    if (listing.StartsWith("drwx") || listing.Contains("<DIR>"))
    {
      Directories.Add(listing.Split(new char[] { ' ' }).Last());
    }
    else
    {
      Filenames.Add(listing.Split(new char[] { ' ' }).Last());
    }
  }
  RaiseFtpDirectoryListedEvent(Directories.ToArray(),
    Filenames.ToArray());
  ...
fileListingData = new List<byte>();
  ftpPassiveOperation = FtpPassiveOperation.ListDirectory;
  logger.AddLog("FTPClient -> LIST\r\n");
  await FtpCommandSocket.SendData("LIST\r\n");
  ...
}

Quando l'elenco di directory è ricevuto tramite il canale dati, viene generato l'evento di FtpDirectoryListed con l'elenco di file e directory nei relativi argomenti di evento (Figura 10 illustrato inviando i comandi PASV e lista, e Figura 11 Visualizza l'output di un elenco di directory):

void ftpClient_FtpDirectoryListed(object sender, 
    FtpDirectoryListedEventArgs e)
{
  foreach (String filename in e.GetFilenames())
  {
    // Handle the name of filenames in current working directory
  }
  foreach (String directory in e.GetDirectories())
  {
    // Handle the name of directories in current working directory
  }
}

Sending LIST Command to the FTP Server
Figura 10 invio elenco comando al Server FTP

Displaying FileSystem Objects in Response to LIST Command
Figura 11 visualizzazione degli oggetti FileSystem in risposta al comando LIST

Che descrive il formato di dati tipo il comando è usato per descrivere il formato di dati in cui dati del file verrà ricevuto o inviato. Non richiede modalità PASV. Io uso "I" formattare con il comando TYPE per significare una trasmissione dati di tipo Image. TIPO è generalmente utilizzato quando l'archiviazione o il recupero di file tramite una connessione dati. Questo dice al server FTP che la trasmissione avverrà in modalità binaria, piuttosto che in modalità testuale o alcuni dati strutturali. Per altre modalità che possono essere utilizzati con il comando di tipo, fare riferimento alla 959 RC per FTP.

Memorizzazione di un File sul FTP Server per archiviare il contenuto di un file (situato su un dispositivo) ad un server FTP, inviare il comando STOR insieme al nome del file (ed estensione) FTP server utilizzerà per creare e salvare il file. La trasmissione del contenuto del file verrà eseguita su una connessione dati quindi, prima di inviare STOR, io sarò una query i dettagli dell'endpoint dal server FTP utilizzando il comando PASV. Dopo aver ricevuto l'endpoint, manderò STOR, e quando ricevo la risposta ad esso, manderò il contenuto del file in formato binario al server FTP tramite la connessione dati.

Il comando STOR può essere inviato chiamando il metodo UploadFileAsync, come mostrato Figura 12. Questo metodo accetta due parametri: l'oggetto Stream del file locale e il nome del file sul server FTP come stringa.

Figura 12 il metodo UploadFileAsync

async private void btnFtp_Tap(object sender, 
    System.Windows.Input.GestureEventArgs e)
{
  ...
if (txtCmd.Text.StartsWith("STOR"))
  {
    logger.Logs.Clear();
    String Filename = txtCmd.Text.Split(new char[] { ' ', '/' },      
      StringSplitOptions.RemoveEmptyEntries).Last();
    StorageFile file =
      await Windows.Storage.ApplicationData.Current.LocalFolder.GetFileAsync(
      txtCmd.Text.Split(new char[] { ' ' },
      StringSplitOptions.RemoveEmptyEntries)[1]);
    await ftpClient.UploadFileAsync(await file.OpenStreamForReadAsync(),
      "video2.mp4");
    return;
  }
  ...
}

Internamente, il metodo UploadFileAsync invia un comando PASV al server FTP per recuperare le informazioni sull'endpoint canale di dati:

public async Task UploadFileAsync(
  System.IO.Stream LocalFileStream, String RemoteFilename)
{
  if (!IsBusy)
  {
    ftpFileInfo = null;
    IsBusy = true;
    ftpFileInfo = new FtpFileOperationInfo(LocalFileStream, 
        RemoteFilename, true);
    ftpCommand = FtpCommand.Type;
    logger.AddLog("FTPClient -> TYPE I\r\n");
    await FtpCommandSocket.SendData("TYPE I\r\n");
  }
}

Come mostrato Figura 13, in risposta del comando PASV all'interno dell'evento DataReceived, viene emesso il comando STOR dopo il canale di dati viene creato e aperto; quindi la trasmissione dei dati inizia dal client al server.

Figura 13 il comando STOR nell'evento DataReceived di presa di elaborazione

async void FtpClientSocket_DataReceived(
  object sender, DataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  ...
IsBusy = true;
  DataWriter dataWriter = new DataWriter(
    FtpDataChannel.OutputStream);
  byte[] data = new byte[32768];
  while (!(await ftpFileInfo.LocalFileStream.ReadAsync(
    data, 0, data.Length)).Equals(0))
  {
    dataWriter.WriteBytes(data);
    await dataWriter.StoreAsync();
    RaiseFtpFileTransferProgressedEvent(
      Convert.ToUInt32(data.Length), true);
  }
  await dataWriter.FlushAsync();
  dataWriter.Dispose();
  dataWriter = null;
  FtpDataChannel.Dispose();
  FtpDataChannel = null;
  ...
await PrepareDataChannelAsync(Response);
  ftpPassiveOperation = FtpPassiveOperation.FileUpload;
  logger.AddLog(String.Format("FTPClient -> STOR {0}\r\n",
    ftpFileInfo.RemoteFile));
  await FtpCommandSocket.SendData(String.Format("STOR {0}\r\n",
    ftpFileInfo.RemoteFile));
  ...
}

Mentre sta caricando il file, il client viene informato del progresso upload tramite FtpFileTransferProgressed:

void ftpClient_FtpFileTransferProgressed(
  object sender, FtpFileTransferProgressedEventArgs e)
{
  // Update the UI with some progressive information
  // or use this to update progress bar
}

Se l'operazione di caricamento di file viene completata correttamente, il FtpFile­evento UploadSucceeded viene generato. Se così non fosse, l'evento FtpFileUploadFailed viene generato con il motivo di fallimento nel suo argomento:

void ftpClient_FtpFileUploadSucceeded(
  object sender, FtpFileTransferEventArgs e)
{
  // Handle UploadSucceeded Event to show some
  // prompt or message to user
}
void ftpClient_FtpFileUploadFailed (
  object sender, FtpFileTransferEventArgs e)
{
  // Handle UploadFailed Event to show some
  // prompt or message to user
}

Figura 14 e Figura 15 visualizzare il processo di caricamento di un file al server FTP.

Uploading a File to the FTP Server
Figura 14 caricamento di un File al Server FTP

Successful File Upload
Figura 15 successo File Upload

Recupero di un File da un FTP Serverper recuperare il contenuto di un file da un server FTP, inviare il comando RETR insieme al nome del file e la sua estensione al server FTP (supponendo che il file è sul server nella directory corrente). La trasmissione del contenuto del file utilizzerà una connessione dati, quindi, prima di inviare RETR, io sarò una query i dettagli dell'endpoint dal server FTP utilizzando il comando PASV. Dopo aver ricevuto l'endpoint, I'll inviare il comando RETR, e dopo aver ricevuto la risposta ad esso, potrai recuperare il contenuto del file in formato binario dal server FTP tramite la connessione dati.

Come mostrato Figura 16, il comando RETR può essere inviato chiamando il metodo DownloadFileAsync. Questo metodo accetta due parametri: l'oggetto Stream del file locale per salvare il contenuto e il nome del file sul server FTP come una stringa.

Figura 16 il metodo DownloadFileAsync

async private void btnFtp_Tap(
  object sender, System.Windows.Input.GestureEventArgs e)
{
  ...
if (txtCmd.Text.StartsWith("RETR"))
  {
    logger.Logs.Clear();
    String Filename = txtCmd.Text.Split(new char[] { ' ', '/' },
      StringSplitOptions.RemoveEmptyEntries).Last();
    StorageFile file =
      await Windows.Storage.ApplicationData.Current.LocalFolder.
CreateFileAsync(
      txtCmd.Text.Split(new char[] { ' ' }, StringSplitOptions.
RemoveEmptyEntries)[1],
      CreationCollisionOption.ReplaceExisting);
    await ftpClient.DownloadFileAsync(
      await file.OpenStreamForWriteAsync(), Filename);
    return;
  }
  ...
}

Internamente, il metodo DownloadFileAsync invia un comando PASV al server FTP per recuperare informazioni sull'endpoint del canale dati:

public async Task DownloadFileAsync(
  System.IO.Stream LocalFileStream, String RemoteFilename)
{
  if (!IsBusy)
  {
    ftpFileInfo = null;
    IsBusy = true;
    ftpFileInfo = new FtpFileOperationInfo(
      LocalFileStream, RemoteFilename, false);
    ftpCommand = FtpCommand.Type;
    logger.AddLog("FTPClient -> TYPE I\r\n");
    await FtpCommandSocket.SendData("TYPE I\r\n");
  }
}

Come mostrato Figura 17, in risposta del comando PASV dentro l'evento DataReceived, viene emesso il comando RETR dopo il canale di dati viene creato e aperto; poi inizia la trasmissione di dati, eseguito dal server al client.

Figura 17 elaborazione il comando RETR nell'evento DataReceived di presa

async void FtpClientSocket_DataReceived(
  object sender, DataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  ...
IsBusy = true;
  DataReader dataReader = new DataReader(FtpDataChannel.InputStream);
  dataReader.InputStreamOptions = InputStreamOptions.Partial;
  while (!(await dataReader.LoadAsync(32768)).Equals(0))
  {
    IBuffer databuffer = dataReader.DetachBuffer();
    RaiseFtpFileTransferProgressedEvent(databuffer.Length, false);
    await ftpFileInfo.LocalFileStream.WriteAsync(
      databuffer.ToArray(), 0, Convert.ToInt32  (databuffer.Length));
  }
  await ftpFileInfo.LocalFileStream.FlushAsync();
  dataReader.Dispose();
  dataReader = null;
  FtpDataChannel.Dispose();
  FtpDataChannel = null;
  ...
await PrepareDataChannelAsync(Response);
  ftpPassiveOperation = FtpPassiveOperation.FileDownload;
  logger.AddLog(String.Format("FTPClient -> RETR {0}\r\n",
    ftpFileInfo.RemoteFile));
  await FtpCommandSocket.SendData(String.Format("RETR {0}\r\n",
    ftpFileInfo.RemoteFile));
  ...
}
}

Mentre sta scaricando il file, il client viene informato del progresso il download tramite FtpFileTransferProgressed:

void ftpClient_FtpFileTransferProgressed(object sender,
  FtpFileTransferProgressedEventArgs e)
{
  // Update the UI with some progressive information or use
  // this to update progress bar
}

Se l'operazione di download di file viene completata correttamente, viene generato l'evento FtpFileDownloadSucceeded. Se così non fosse, l'evento FtpFileDownloadFailed viene generato con il motivo di fallimento nel suo argomento:

void ftpClient_FtpFileDownloadSucceeded(object sender,
  FtpFileTransferFailedEventArgs e)
{
  // Handle DownloadSucceeded event to show some prompt
  // or message to user
}
void ftpClient_FtpFileDownloadFailed(object sender,
  FtpFileTransferFailedEventArgs e)
{
  // Handle UploadFailed Event to show some prompt
  // or message to user
}

Figura 18 e Figura 19 visualizzare il processo di download di un file dal server FTP.

Downloading a File from the FTP Server
Figura 18 scaricando un File dal Server FTP

Successful File Download
Figura 19 successo File Download

Conclusioni

Si noti che è possibile fornire un'associazione di URI per un app in esecuzione FTP, in modo che altre applicazioni possono anche accedere ai dati di servizio e richiesta FTP dal server FTP. Troverete ulteriori informazioni su questa pagina del Windows Phone Dev Center, "avvio automatico app file e associazioni URI per Windows Phone 8," a bit.ly/XeAaZ8e il codice di esempio in bit.ly/15x4O0y.

Il codice che ho scritto per la mia libreria FTP e client app per Windows Phone è completamente supportato su Windows 8. x, come non ho usato qualsiasi API che non è compatibile con Windows 8. x. È possibile sia ricompilare il codice per Windows 8. x, o metterlo in un portatile classe Library (PCL) che possono indirizzare entrambe le piattaforme.

Uday Gupta è ingegnere senior - sviluppo prodotto per Sinfonia Teleca Corp. (India) Pvt Ltd Ha esperienza in molte tecnologie .NET, soprattutto in Windows Presentation Foundation (WPF), Silverlight, Windows Phone e Windows 8. La maggior parte del suo tempo viene speso in codifica, gioco, imparare cose nuove e aiutare gli altri.

Grazie ai seguenti esperti tecnici per la revisione di questo articolo: Tony campione (campione del DS) e Andy Wigley (Microsoft)
Tony Champion è Presidente del campione del DS, è un Microsoft MVP e attivo nella comunità come un altoparlante, blogger e autore. Egli mantiene un blog a tonychampion.net e può essere raggiunto via e-mail a tony@tonychampion.net.

Andy Wigley è un Technical Evangelist lavora per Microsoft UK. Andy è ben nota per il popolare video Windows Phone JumpStart che sono disponibili su msdn.com ed è regolare relatore in conferenze principali quali Tech Ed.