Sauron Software Projects Repository
ftp4j
Presentazione Documentazione Download

Manuale di ftp4j 1.7

Requisiti

Per eseguire la libreria ftp4j è necessario un Java Runtime Environment J2SE v. 1.4 o successivo.

Installazione

Si aggiunga il file JAR di ftp4j al classpath dell'applicazione che deve richiamarlo.

Documentazione javadoc

ftp4j api

Per cominciare

La classe principale della libreria è FTPClient (it.sauronsoftware.ftp4j.FTPClient).

Si inizia creando un'istanza di FTPClient:

FTPClient client = new FTPClient();

Il client deve poi essere connesso ad un servizio FTP remoto:

client.connect("ftp.host.com");

Se la porta del servizio FTP remoto non è quella standard, cioè la 21:

client.connect("ftp.host.com", port);

Ad esempio:

client.connect("ftp.host.com", 8021);

Il protocollo FTP richiede ora una fase di autenticazione:

client.login("carlo", "mypassword");

Se nessuna eccezione viene propagata il client è ora autenticato per l'uso del servizio. In caso contrario, se il tentativo di accesso fallisce, si riceve una it.sauronsoftware.ftp4j.FTPException.

L'accesso anonimo, se consentito dal server, può essere svolto inviando lo username "anonymous" ed una password qualsiasi (attenzione al fatto che alcuni server, come password per l'accesso anonimo, richiedono un indirizzo e-mail):

client.login("anonymous", "ftp4j");

Una volta completate le operazioni è necessario disconnettere il client:

client.disconnect(true);

Questa formula invia il comando QUIT al server FTP, richiedendo una procedura di disconnessione legale in base al protocollo. Per interrompere la connessione senza eseguire la procedura legale di disconnessione, è possibile richiamare:

client.disconnect(false);

Connessione attraverso un proxy

Il client di ftp4j si connette al server attraverso un connettore (un oggetto che estende la classe astratta it.sauronsoftware.ftp4j.FTPConnector). Il client carica il connettore che gli è stato assegnato e gli domanda la connessione con il server. Il connettore stabilisce la connessione e ne restituisce il controllo al client (attraverso un oggetto che implementa l'interfaccia it.sauronsoftware.ftp4j.FTPConnection). Grazie a questo meccanismo di astrazione ftp4j è in grado di supportare la connessione attraverso diversi tipi di proxy.

Il connettore di un client può essere impostato attraverso il metodo setConnector(), naturalmente prima di connettere il client al server.

client.setConnector(anyConnectorYouWant);

Se nessun connettore viene impostato, il client usa quello predefinito, che è DirectConnector (it.sauronsoftware.ftp4j.connectors.DirectConnector). DirectConnector stabilisce una connessione TCP diretta tra il client ed il server, senza altre destinazioni o protocolli intermediari.

Se il server FTP remoto può essere raggiunto solo passando attraverso un proxy, allora bisogna cambiare il connettore. Con ftp4j sono forniti diversi connettori, in grado di negoziare la connessione con i più comuni tipi di proxy:

Poiché l'architettura di ftp4j è modulare, altri connettori possono essere sviluppati da chiunque, estendendo la classe astratta FTPConnector.

Connessione sicura FTPS/FTPES

La libreria ftp4j supporta le due varianti sicure di FTP denominate FTPS (FTP con TLS/SSL implicito) e FTPES (FTP su TLS/SSL esplicito).

Il metodo setSecurity() può essere utilizzato per attivare la funzionalità:

client.setSecurity(FTPClient.SECURITY_FTPS); // attiva FTPS
client.setSecurity(FTPClient.SECURITY_FTPES); // attiva FTPES

Ambo i metodi devono essere invocati prima di connettere il client al server.

Se il livello di sicurezza è impostato su SECURITY_FTPS, la porta di default usata dal metodo connect() diventa 990.

Il client negozia le connessioni SSL usando la socket factory restituita dalla chiamata Java javax.net.ssl.SSLSocketFactory.getDefault(). La socket factory può essere cambiata invocando il metodo del client setSSLSocketFactory(). Una SSLSocketFactory alternativa, per esempio, può essere usata per fidarsi di qualunque certificato inviato dalla parte remota, anche se non riconosciuto o scaduto (da usare con cautela):

import it.sauronsoftware.ftp4j.FTPClient;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

// ...

TrustManager[] trustManager = new TrustManager[] { new X509TrustManager() {
	public X509Certificate[] getAcceptedIssuers() {
		return null;
	}
	public void checkClientTrusted(X509Certificate[] certs, String authType) {
	}
	public void checkServerTrusted(X509Certificate[] certs, String authType) {
	}
} };
SSLContext sslContext = null;
try {
	sslContext = SSLContext.getInstance("SSL");
	sslContext.init(null, trustManager, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
	e.printStackTrace();
} catch (KeyManagementException e) {
	e.printStackTrace();
}
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
FTPClient client = new FTPClient();
client.setSSLSocketFactory(sslSocketFactory);
client.setSecurity(FTPClient.SECURITY_FTPS); // o client.setSecurity(FTPClient.SECURITY_FTPES);

// ...

Navigare nel sito remoto

Per recuperare il percorso della directory di lavoro corrente:

String dir = client.currentDirectory();

Per cambiare la directory di lavoro:

client.changeDirectory(newPath);

Possono essere usati sia i percorsi assoluti (rispetto alla radice del sito) sia quelli relativi:

client.changeDirectory("/an/absolute/one");
client.changeDirectory("relative");

Per spostarsi nella directory superiore, cioè quella che contiene la directory di lavoro corrente:

client.changeDirectoryUp();

Rinominare i file e le directory

Per rinominare un file o una directory:

client.rename("oldname", "newname");

Spostare i file e le directory:

Il metodo rename() può essere usato anche per spostare i file e le directory da una posizione ad un'altra.

Si immagini di avere nella directory di lavoro corrente un file chiamato "myfile.txt", che deve essere spostato nella sotto-directory "myfolder":

client.rename("myfile.txt", "myfolder/myfile.txt");

Cancellare i file

Per cancellare un file remoto:

client.deleteFile(relativeOrAbsolutePath);

Ad esempio:

client.deleteFile("useless.txt");

Creare e cancellare le directory

Per creare una nuova directory nel sito remoto, ammesso che i diritti di accesso lo consentano:

client.createDirectory("newfolder");

Per cancellare una directory esistente:

client.deleteDirectory(absoluteOrRelativePath);

Ad esempio:

client.deleteDirectory("oldfolder");

Si faccia attenzione al fatto che, generalmente, i servizi FTP si rifiutano di cancellare le directory non vuote.

Elencare i file, le directory ed i collegamenti

Il protocollo FTP non specifica alcun metodo ampiamente supportato per elencare i dettagli del contenuto di una directory. La cosa che ci va più vicina è il comando LIST, ma che purtroppo lascia al server libera scelta sul formato della risposta. Così accade che alcuni server rispondono con un output che ricorda quello del comando ls di UNIX, altri come il dir di DOS, altri ancora con formati alternativi ai due.

La libreria ftp4j può riconoscere diversi formati. Una volta interpretata la risposta in base al formato riconosciuto, al programmatore viene restituita una rappresentazione ad oggetti di quanto elaborato. Attualmente ftp4j può riconoscere ed interpretare gli stili del tipo:

Tutto ciò viene fatto attraverso degli interpreti modulari. Il pacchetto it.sauronsoftware.ftp4j.listparsers contiene gli interpreti in grado di gestire i formati appena elencati. Il più delle volte sono sufficienti.

Per elencare il contenuto della directory di lavoro corrente:

FTPFile[] list = client.list();

Se si riceve una FTPListParseException (it.sauronsoftware.ftp4j.FTPListParseException) significa che nessuno degli interpreti di ftp4j è riuscito a decodificare la risposta fornita dal server. Se dovesse capitare, si può provare ad usare il metodo alternativo listNames(), che però restituisce molte meno informazioni rispetto a list(). A mali estremi, estremi rimedi: è possibile costruire un nuovo interprete in grado di gestire il particolare formato riscontrato, estendendo l'interfaccia FTPListParser (it.sauronsoftware.ftp4j.FTPListParser). L'istanza del nuovo interprete può essere aggiunta al client con il metodo addListParser().

Gli oggetti FTPFile (it.sauronsoftware.ftp4j.FTPFile) mantengono una rappresentazione dei contenuti della directory di lavoro, e sono usati per rappresentare i file, le sotto-directory ed i collegamenti. In base al tipo di risposta dato dal server alcune informazioni di un oggetto FTPFile possono essere assenti o impostate su valori di poco senso. Si faccia riferimento ai javadoc della libreria per maggiori dettagli.

E' possibile filtrare i risultati restituiti dal metodo list(), come nel seguente esempio:

FTPFile[] list = client.list("*.jpg");

Se il server dichiara esplicitamente di supportare il comando MLSD, ftp4j lo usa al posto del comando LIST. Gli elenchi MLSD, infatti, sono standard e per questo permettono un'interpretazione più accurata. Purtroppo non tutti i server supportano questo standard, ed alcuni lo supportano malamente. Si può controllare il comportamento di ftp4j rispetto all'uso del comando MLSD attraverso il metodo setMLSDPolicy() di FTPClient. I valori possibili sono:

Ad esempio:

client.setMLSDPolicy(FTPClient.MLSD_NEVER);

Data di modifica di file, directory e collegamenti

Normalmente un oggetto FTPFile riporta la data di modifica dell'entità rappresentata, ma come si è appena detto il dettaglio e la completezza della risposta dipendono dalla tipologia del server remoto. Quando l'informazione manca si può provare a recuperarla in questo modo:

java.util.Date md = client.modifiedDate("filename.ext");

Download ed upload dei file

La maniera più semplice per scaricare un file è chiamare il metodo download(String, File):

client.download("remoteFile.ext", new java.io.File("localFile.ext"));

L'analogo per l'upload è:

client.upload(new java.io.File("localFile.ext"));

L'upload in modalità appending è possibile facendo:

client.append(new java.io.File("localFile.ext"));

Si faccia attenzione al fatto che queste sono chiamate bloccanti: il metodo ritorna solo dopo che il trasferimento è stato completato (oppure interrotto, o anche fallito). Durante il trasferimento, inoltre, un lock viene imposto sull'intero client e quindi anche usando più thread non è proprio possibile fare upload e download di più file simultaneamente con un solo oggetto FTPClient. Questo limite è stato imposto perché il protocollo FTP ammette un solo trasferimento alla volta per sessione. Sicuramente, però, sarà capitato di osservare dei client FTP in grado di trasferire simultaneamente due o più file. Ciò viene ottenuto stabilendo più sessioni con lo stesso sito remoto. Naturalmente è possibile farlo anche con ftp4j, istanziando e connettendo allo stesso sito più oggetti FTPClient. Lato-client nessun limite in tal senso viene imposto, si faccia però attenzione al fatto che lato-server può esserci una regola che limita il numero di connessioni ricevibili da parte del medesimo host.

Piuttosto che attendere il completamento di un trasferimento senza fare nulla, può essere preferibile monitorare il progresso dell'operazione. Può essere fatto mediante un FTPDataTransferListener (it.sauronsoftware.ftp4j.FTPDataTransferListener). Si implementi il proprio:

import it.sauronsoftware.ftp4j.FTPDataTransferListener;

public class MyTransferListener implements FTPDataTransferListener {

	public void started() {
		// Trasferimento avviato
	}

	public void transferred(int length) {
		// Altri length byte sono stati trasferiti da quando questo metodo
		// è stato richiamanto l'ultima volta
	}

	public void completed() {
		// Trasferimento completato
	}

	public void aborted() {
		// Trasferimento annullato
	}

	public void failed() {
		// Trasferimento fallito
	}

}

Adesso si trasferisca come segue:

client.download("remoteFile.ext", new java.io.File("localFile.ext"), new MyTransferListener());
client.upload(new java.io.File("localFile.ext"), new MyTransferListener());
client.append(new java.io.File("localFile.ext"), new MyTransferListener());

Mentre il client sta trasferendo un file, l'operazione può essere interrotta da un altro thread che richiama il metodo abortCurrentDataTransfer() sul medesimo oggetto FTPClient. Il metodo richiede un parametro booleano: true per negoziare l'annullamento del trasferimento con il server (mediante il comando ABOR), false per chiudere bruscamente il canale di trasferimento senza darne previa comunicazione al server.

client.abortCurrentDataTransfer(true); // Invia ABOR
client.abortCurrentDataTransfer(false); // Interrompre bruscamente

Si osservi che anche i metodi list() e listNames() implicameno un trasferimento (la risposta del server viene fornita su un canale di trasferimento secondario, e non lungo la linea di comunicazione principale). Per questo motivo il metodo abortCurrentDataTransfer() può interrompere anche una procedura di tipo list.

Quando un trasferimento viene interrotto su comando del client, i metodi download(), upload(), append(), list() e listNames() terminano lanciando una FTPAbortedException (it.sauronsoftware.ftp4j.FTPAbortedException).

Le operazioni di download ed upload non completate possono essere riprese fornendo un parametro restartAt:

client.download("remoteFile.ext", new java.io.File("localFile.ext"), 1056);

Questo esempio riprende l'operazione a partire dal byte 1056 del file. Il primo byte trasferito sarà il numero 1057.

Altre varianti di download(), upload() ed append() lavorano con gli stream invece che con gli oggetti java.io.File. In questa maniera è possibile scambiare i dati non solo con il file system, ma anche con un database, una connessione di rete o altro ancora.

Trasferimenti attivi e passivi

I canali di trasferimento sono stabiliti su una separata connessione di rete tra il client ed il server. Chi contatta l'altro? La risposta dipende dal modo adottato. Il server può essere attivo o passivo. Quando è attivo il server si connette al client per trasferire dei dati; quando è passivo è il client a dover stabilire la connessione.

Il trasferimento attivo può essere utilizzato solo se il client è in grado di ricevere connessioni da parte del server. Se il client è dietro un firewall, un proxy, un gateway o un misto di questi è molto poco probabile che il server possa raggiungerlo per stabilire una connessione. Perciò di solito si preferisce chiedere al server di operare in maniera passiva, in modo che sia sempre e solo il client a svolgere le connessioni attraverso il proprio connettore.

Con ftp4j è possibile scegliere il modo in cui sarà stabilita la connessione per un trasferimento:

client.setPassive(false); // Server attivo
client.setPassive(true); // Server passivo

Il valore predefinito è true, cioè al server viene chiesto di restare passivo in modo che sia sempre il client a connettersi.

Durante la negoziazione di un trasferimento di file passivo, il server comunica al client un indirizzo IP ed un numero di porta. Il client dovrebbe usare questi dati per connettersi ed eseguire lo scambio file. In ambienti di produzione aziendali, tuttavia, questo comportamento richiesto dalla specifica FTP risulta spesso problematico. La presenza di firewall, proxy e configurazioni NAT, infatti, determinano spesso delle condizioni per cui l'indirizzo IP comunicato dal server non risulta direttamente raggiungibile partendo dal client. Questo è il motivo per cui molti client FTP non rispettano la specifica ed ignorano l'indirizzo IP comunicato dal server, connettendosi invece automaticamente allo stesso host con il quale hanno stabilito la linea di comunicazione principale. Anche la libreria ftp4j opta solitamente per questo approccio, tuttavia la caratteristica è ampiamente configurabile. Per capire cosa farà ftp4j, bisogna prendere in considerazione i seguenti elementi:

Quando il modo di trasferimento è impostato su attivo, le seguenti proprietà di sistema possono essere impostate:

Per impostare una proprietà di sistema si può:

Trasferimenti binari e testuali

Un altro concetto legato ai trasferimenti riguarda i tipi binari e testuali. Quando un trasferimento è binario il file viene trattato da ambo le parti come una sequenza binaria, cioè un flusso di byte, e viene registrato da chi lo riceve così come gli è arrivato, senza analisi o manipolazioni dei dati ricevuti. Quando un trasferimento è testuale, al contrario, ambo le parti possono applicare delle trasformazioni di charset. Si immagini di avere un client in esecuzione su una piattaforma Windows, mentre il server gira su un sistema UNIX. Normalmente i charset usati dalle due piattaforme sono diversi. Il client deve inviare al sito remoto un file dai contenuti testuali. Scegliendo il trasferimento testuale il client leggerà il file supponendo che i suoi contenuti siano stati codificati secondo il charset predefinito della macchina che lo esegue. Il contenuto del file sarà decodificato e ricodificato in una codifica intermedia. Il server riceverà i dati nella codifica intermedia, e sarà suo compito riconvertirli secondo il charset predefinito della propria macchina. Se i charset di partenza e di destinazione sono differenti la sequenza di byte che compone il file trasferito sarà molto probabilmente cambiata, ma il contenuto rimarrà lo stesso e la leggibilità mantenuta.

Per scegliere il tipo del trasferimento con ftp4j:

client.setType(FTPClient.TYPE_TEXTUAL);
client.setType(FTPClient.TYPE_BINARY);
client.setType(FTPClient.TYPE_AUTO);

La costante TYPE_AUTO chiede al client di scegliere di volta in volta quale tipo di trasferimento negoziare con il server. Il client individua la natura del file da trasferire in base alla sua estensione, e lo fa attraverso un FTPTextualExtensionRecognizer (it.sauronsoftware.ftp4j.FTPTextualExtensionRecognizer). Il riconoscitore di estensioni testuali predefinito è un'istanza di it.sauronsoftware.ftp4j.recognizers.DefaultTextualExtensionRecognizer, che riconosce come testuali le seguenti estensioni:

abc acgi aip asm asp c c cc cc com conf cpp
csh css cxx def el etx f f f77 f90 f90 flx
for for g h h hh hh hlb htc htm html htmls
htt htx idc jav jav java java js ksh list
log lsp lst lsx m m mar mcf p pas php pl pl
pm py rexx rt rt rtf rtx s scm scm sdml sgm
sgm sgml sgml sh shtml shtml spc ssi talk
tcl tcsh text tsv txt uil uni unis uri uris
uu uue vcs wml wmls wsc xml zsh

E' possibile costruire altri riconoscitori, implementando l'interfaccia FTPTextualExtensionRecognizer, ma è molto più conveniente istanziare la classe ParametricTextualExtensionRecognizer (it.sauronsoftware.ftp4j.recognizers.ParametricTextualExtensionRecognizer), che permette al programmatore di specificare liberamente la lista delle estensioni da riconoscere come testuali. In ogni caso un'istanza del riconoscitore va poi registrata presso il client:

client.setTextualExtensionRecognizer(myRecognizer);

Compressione dei trasferimenti

Alcuni server supportano la compressione dei trasferimenti attraverso una funzionalità chiamata MODE Z. Questa funzionalità fa risparmiare tempo e banda quando si scambiano file molto grandi e non compressi all'origine. Dopo che il client si è connesso ed autenticato, si può verificare se il server supporta la compressione chiamando:

boolean compressionSupported = client.isCompressionSupported();

Se la compressione è supportata, la si può abilitare:

client.setCompressionEnabled(true);

Tutti i trasferimenti successivi a questa chiamata (download, upload e operazioni di scansione delle directory) saranno compressi, risparmiando banda.

In qualsiasi momento la compressione può essere nuovamente disabilitata, chiamando:

client.setCompressionEnabled(false);

Per controllare se la compressione è abilitata è possibile fare così:

boolean compressionEnabled = client.isCompressionEnabled();

Attenzione: i trasferimenti saranno realmente compressi se e solo se la compressione è sia abilitata sia supportata dal server. Se la compressione è abilitata, ma non supportata dal server, i trasferimenti avranno luogo in maniera non compressa.

Please note that compressed data transfer will take place only if compression is both supported and enabled.

Di default la compressione non è abilitata, anche quando il server dichiara di supportarla. Se si vuole usare la compressione, quindi, bisogna sempre attivarla esplicitamente.

Il comando NOOP

Si immagini che il client è fermo senza fare nulla perché in attesa di input da parte dell'utente. Può accadere che il server, non riscontrando alcuna attività per un periodo troppo lungo, chiuda la connessione con il client. Non appena il client tenterà un'operazione questa fallirà, poiché la linea di comunicazione è stata chiusa unilateralmente. Per evitare lo scadere del timeout di inattività è possibile inviare di tanto in tanto, durante i periodi di stasi, un comando NOOP al server. Questo genere di comando non esegue alcuna reale operazione, ed ha il solo effetto di far sapere al server che il client è ancora attivo.

Con ftp4j è sufficiente chiamare:

client.noop();

Operazioni NOOP possono anche essere eseguite automaticamente dal client allo scadere di un certo periodo di inattività. Inizialmente questa caratteristica è disabilitata. La si può attivare invocando il metodo setAutoNoopTimeout(), attraverso il quale va impostata la durata del timeout di inattività con un valore espresso in millisecondi. Ad esempio:

client.setAutoNoopTimeout(30000);

In questa maniera, il client invia un comando NOOP al server dopo 30 secondi di inattività.

L'invio automatico dei comandi NOOP può essere nuovamente disabilitato usando un valore minore o uguale a zero:

client.setAutoNoopTimeout(0);

Comandi del sito e personalizzati

Per inviare un comando specifico del sito:

FTPReply reply = client.sendSiteCommand("YOUR COMMAND");

Per inviare comandi personalizzati:

FTPReply reply = client.sendCustomCommand("YOUR COMMAND");

Sia sendSiteCommand() che sendCustomCommand() restituiscono un oggetto FTPReply (it.sauronsoftware.ftp4j.FTPReply). Attraverso questo oggetto è possibile controllare la risposta ricevuta, recuperando il codice ed il messaggio che la costituiscono. L'interfaccia FTPCodes (it.sauronsoftware.ftp4j.FTPCodes) riporta i codici comuni delle risposte FTP, così che è possibile andare a raffrontare il codice di una FTPReply senza dover consultare le specifiche FTP.

Gestione delle eccezioni

La libreria ftp4j definisce cinque tipi di eccezione:

© Sauron Software 2007 - 2012 | This page in English