Sauron Software Projects Repository
ftp4j
Overview Documentation Download

ftp4j 1.7 manual

Requirements

To run the ftp4j library you need a Java Runtime Environment J2SE v. 1.4 or later.

Installation

Add the ftp4j JAR file to your application classpath, and you'll be automatically enabled to the use of the ftp4j classes.

Javadocs

Here come the ftp4j javadocs.

Quickstart

The main class of the library is FTPClient (it.sauronsoftware.ftp4j.FTPClient).

Start creating a FTPClient instance:

FTPClient client = new FTPClient();

Connect now to a remote FTP service:

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

If the service port is other than the standard 21 (or 990 if FTPS):

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

In example:

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

Step now to the login procedure:

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

If no exception is thrown you are now authenticated to the remote server. Otherwise, if the authentication attempt fails, you receive a it.sauronsoftware.ftp4j.FTPException.

Anonymous authentication, if admitted by the connected service, can be done sending the username "anonymous" and an arbitrary password (note that some servers require an e-mail address in place of the password):

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

Do anything you want with the remote FTP service, then disconnect:

client.disconnect(true);

This one sends the FTP QUIT command to the remote server, requesting a legal disconnect procedure. If you just want to break the connection, without sending any advice to the server, call:

client.disconnect(false);

Connecting through a proxy

The client connects to the server through a connector (an object extending it.sauronsoftware.ftp4j.FTPConnector), which returns to the client an already open connection (an object implementing the it.sauronsoftware.ftp4j.FTPConnection interface). That is why ftp4j could support a large set of proxies.

The connector for a client instance can be setted with the setConnector() method, obviously before connecting the remote server:

client.setConnector(anyConnectorYouWant);

The default connector, which is used if no other is setted, is DirectConnector (it.sauronsoftware.ftp4j.connectors.DirectConnector), which performs a direct connection to the remote server, without asking the connection to any proxy.

If you can connect the remote server only through a proxy, the ftp4j library let you choose between some other connectors:

Since the connector architecture used by ftp4j is a pluggable one, you can always build your own connector extending the FTPConnector abstract class.

FTPS/FTPES secured connection

The ftp4j library supports both FTPS (FTP over implicit TLS/SSL) and FTPES (FTP over explicit TLS/SSL).

The setSecurity() method can be used to turn on the feature:

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

Both methods must be called before connecting the remote server.

If the security is set to SECURITY_FTPS, the default port used by the connect() method changes to 990.

The client object, by default, negotiates SSL connections using the SSL socket factory provided by javax.net.ssl.SSLSocketFactory.getDefault(). The default socket factory can be changed calling the client setSSLSocketFactory() method. An alternative SSLSocketFactory, for example, can be used to trust every certificate given by the remote host (use it carefully):

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); // or client.setSecurity(FTPClient.SECURITY_FTPES);

// ...

Browsing the remote site

Get the current directory absolute path calling:

String dir = client.currentDirectory();

Change directory with:

client.changeDirectory(newPath);

You can use both absolute and relative paths:

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

Back to the parent directory with:

client.changeDirectoryUp();

Renaming files and directories

To rename a remote file or directory:

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

Moving files and directories

The rename() method can also be used to move files and directories from a location to another.

In example, think in the current working directory you have a file called "myfile.txt", and you want to move it in the sub-directory "myfolder":

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

Deleting files

To delete a remote file call:

client.deleteFile(relativeOrAbsolutePath);

In example:

client.deleteFile("useless.txt");

Creating and deleting directories

You can create a new directory on the remote site, if the service gives you this oppurtunity:

client.createDirectory("newfolder");

You can also remove an existing one:

client.deleteDirectory(absoluteOrRelativePath);

In example:

client.deleteDirectory("oldfolder");

Please note that usually FTP servers can delete only empty directories.

Listing files, directories and links

The FTP protocol doesn't offer a wide supported method to get complete informations about the contents of the working directory. The LIST command usually gives all you need to know, but unfortunately every server can use a different style for the response. It means that some servers return a UNIX style directory listing, some servers prefer the DOS style, others use some alternative ones.

The ftp4j library can handle many LIST response formats, building from them a unified structured object representation of the directory contents. Currently ftp4j can handle:

This is done using pluggable parsers. The package it.sauronsoftware.ftp4j.listparsers contains the ones handling the styles listed above. Most of the time this should be enough.

To list the current working directory entries call:

FTPFile[] list = client.list();

If you receive a FTPListParseException (it.sauronsoftware.ftp4j.FTPListParseException) it means that the server has replied to the LIST command in an incomprehensible style, that is none of the ones listed above. So you can try with the listNames() method, but that is less profitable than the list() one. Extremis malis extrema remedia: build your own LIST response parser, supporting the style you have encountered. You can do that implementing the FTPListParser (it.sauronsoftware.ftp4j.FTPListParser) interface. Then you can plug an instance of your parser in the client calling the addListParser() method.

FTPFile (it.sauronsoftware.ftp4j.FTPFile) objects offer a representation of the directory contents, including files, sub-directories and links. Depending on the response supplied by the server, some fields of a FTPFile object could be null or setted to non-sense values. Please check the javadocs for details.

You can also use a file filter parameter with the list() method, i.e.:

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

If the connected server declares explicitly to support the special MLSD command, ftp4j will use it instead of the basic LIST command. MLSD responses, infact, are standard, accurated and more easily parsable. Unfortunately not all of the servers support this command, and some of them support it badly. For these reasons the developer can control whether ftp4j should use the MLSD command by calling the setMLSDPolicy() method of a FTPClient object. Admitted values are:

For example:

client.setMLSDPolicy(FTPClient.MLSD_NEVER);

Getting the modification date of files and directories

Usually a FTPFile object tells you about the last modification date of an entry, but as described above, that depends on the reply sent by the server. If you need a modification date and you can't get it through the list() method, try this:

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

Downloading and uploading files

The easiest way to download a remote file is a call to the download(String, File) method:

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

To upload:

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

To upload appending contents to an existing file:

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

These are blocking calls: they will return only when the transfer will be completed (or failed, or aborted). Moreover a synchronization lock is imposed on the client, since only a transfer per time is permitted in a regular FTP communication. You can handle multiple transfers per time using several FTPClient objects, each one establishing a separate connection with the server.

You can monitor transfers with FTPDataTransferListener (it.sauronsoftware.ftp4j.FTPDataTransferListener) objects. Implement your one:

import it.sauronsoftware.ftp4j.FTPDataTransferListener;

public class MyTransferListener implements FTPDataTransferListener {

	public void started() {
		// Transfer started
	}

	public void transferred(int length) {
		// Yet other length bytes has been transferred since the last time this
		// method was called
	}

	public void completed() {
		// Transfer completed
	}

	public void aborted() {
		// Transfer aborted
	}

	public void failed() {
		// Transfer failed
	}

}

Now download or upload as follows:

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());

While the client is downloading or uploading, the transfer can be aborted by another thread calling the abortCurrentDataTransfer() method on the same FTPClient object. This one requires a boolean parameter: true to perform a legal abort procedure (an ABOR command is sent to the server), false to abruptly close the transfer without advice:

client.abortCurrentDataTransfer(true); // Sends ABOR
client.abortCurrentDataTransfer(false); // Breaks abruptly

Note that also the list() and the listNames() methods imply a data transfer (the response is served on a data transfer channel), so the abortCurrentDataTransfer() method can also be used to interrupt a list procedure.

When a data tranfer is aborted the download(), upload(), append(), list() and listNames() methods die throwing a FTPAbortedException (it.sauronsoftware.ftp4j.FTPAbortedException).

Download and upload operation can be resumed suppling a restartAt parameter:

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

This one resumes the download operation starting from the 1056th byte of the file. The first byte transferred will be the 1057th.

Other download(), upload() and append() variants let you work with streams instead of java.io.File objects. So you can also transfer data from and to a database, a network connection or anything else.

Check the javadocs.

Active and passive data transfer modes

Data transfer channels are established through a separate network connection between the client and the server. The server could be active or passive in the establishing of the transfer channel. When the server is active data transfers work as follows:

  1. The client sends to the server its IP address and a port number.
  2. The client asks to the server a data transfer, and it starts listening the port sent before.
  3. The server connects the address and the port supplied by the client.
  4. The data transfer starts in the new established channel.

The active mode requires that your client could receive incoming connections from the server. If your client is behind a firewall, a proxy, a gateway or a mix of them, most of the time that is a problem, since it cannot receive incoming connections from outside. Here comes the passive data transfer mode:

  1. The client asks to the server to prepare a passive data transfer.
  2. The server replies with its IP address and a port number.
  3. The client asks the transfer and connects.
  4. The data transfer starts in the new established channel.

In passive mode the client connects the server: no incoming connection is required.

With ftp4j you can switch between the active and passive modes calling:

client.setPassive(false); // Active mode
client.setPassive(true); // Passive mode

The default value for a ftp4j client passive flag is true: if you never call setPassive(false) your client will act ever asking the passive mode to the server before every transfer.

When a passive file transfer is being negotiated, the server supplies an IP address and a port number. The client, as of FTP specifications, should connect to the given host and port. In business environments, this behavior could be often problematical, since NAT configurations could prevent literal connections to the given IP address. This is the reason why FTP clients usually ignore any IP address returned by the server and connects to the same host used for the communication line. The ftp4j's behavior depends on several factors:

In the active transfer mode, the following system properties can be set:

To set a system property you can:

Binary and textual data transfer types

Another data transfer key concept concerns the binary and the textual types. When a transfer is binary the file is treated as a binary stream, and it is stored by the target machine as it is received from the source. A textual data transfer, instead, treats the transferred file as a character stream, performing charset transformation. Suppose your client is running on a Windows platform, while the server runs on UNIX, whose default charsets are usually different. The client send a file to the server selecting textual type. The client assumes that the file is encoded with the machine standard charset, so it decodes every character and encodes it in an intermediate charset before sending. The server receives the stream, decode the intermediate charset and encodes the file with its machine default charset before storing. Bytes has been changed, but contents are the same.

You can choose your transfer type calling:

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

The TYPE_AUTO constant, which is also the default one, let the client pick the type automatically: a textual transfer will be performed if the extension of the file is between the ones the client recognizes as textual type markers. File extensions are sniffed through a FTPTextualExtensionRecognizer (it.sauronsoftware.ftp4j.FTPTextualExtensionRecognizer) instance. The default extension recognizer, which is an instance of it.sauronsoftware.ftp4j.recognizers.DefaultTextualExtensionRecognizer, recognizes these extensions as textual ones:

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

You can build your own recognizer implementing the FTPTextualExtensionRecognizer interface, but maybe you'll like more to instance the convenience class ParametricTextualExtensionRecognizer (it.sauronsoftware.ftp4j.recognizers.ParametricTextualExtensionRecognizer). Anyway, don't forget to plug your recognizer in the client:

client.setTextualExtensionRecognizer(myRecognizer);

Data transfer compression

Some servers support a data transfer compression feature called MODE Z. This feature is useful to save bandwidth on large file transfers. Once the client is connected to the server and authenticated, the compression support can be checked by calling:

boolean compressionSupported = client.isCompressionSupported();

If compression is supported on the server-side, it can be enabled by calling:

client.setCompressionEnabled(true);

After this call, any subsequent data transfer (downloads, uploads and list operations) will be compressed, saving bandwidth.

Data transfer compression can be disabled again by calling:

client.setCompressionEnabled(false);

The flag value can also be checked:

boolean compressionEnabled = client.isCompressionEnabled();

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

By default, compression is disabled, even if supported by the server. If required, it has to be explicitly turned on.

NOOPing the server

Suppose your client is doing nothing since it's waiting for user input. Usually FTP servers disconnect automatically an inactive client. To avoid this timeout you can send now and then a NOOP command. This one does nothing but it points out to the server that the client is still alive, resetting the timeout counter. Call:

client.noop();

Automatic NOOPs can also be issued by the client when an inactivity timeout occurs. By default this feature is disabled. It can be enabled by setting the timeout duration with the setAutoNoopTimeout() method, supplying a value expressed in milliseconds. For example:

client.setAutoNoopTimeout(30000);

With this value, the client will issue a NOOP command after 30 seconds of inactivity.

The automatic NOOP timeout can be disabled again by using a value equal or less than zero:

client.setAutoNoopTimeout(0);

Site specific and custom commands

You can send site specific commands as follows:

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

You can also sends custom commands:

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

Both sendSiteCommand() and sendCustomCommand() return a FTPReply (it.sauronsoftware.ftp4j.FTPReply) object. With this one you can check the response received, getting the server reply code and message. The FTPCodes (it.sauronsoftware.ftp4j.FTPCodes) interface reports some common FTP reply codes, so you can try to match your reply code with one of those the library knows for sure.

Exceptions handling

The ftp4j library defines five kinds of exception:

© Sauron Software 2007 - 2012 | Questa pagina in italiano