要求
要运行ftp4j library,你需要Java 运行时环境v. 1.4+.
安装
将ftp4j JAR文件添加到你应用程序的classpath中, 然后你就可以自动启用ftp4j类的使用了.
Javadocs
可参考ftp4j javadocs.
快速入门
包中的主类是FTPClient (it.sauronsoftware.ftp4j.FTPClient).
创建一个FTPClient 实例:
FTPClient client = new FTPClient();
连接远程FTP服务:
client.connect("ftp.host.com");
如果服务端口不是标准的21端口 (或 FTPS的990端口),需要使用port参数进行指定:
client.connect("ftp.host.com", port);
如:
client.connect("ftp.host.com", 8021);
然后进行登录流程:
client.login("carlo", "mypassword");
如果没有抛出任何异常的话,那么你就通过远程服务器的认证了.否则,如果验证失败,你将会收到it.sauronsoftware.ftp4j.FTPException异常.
匿名认证,如果被连接服务认可的话, 可通过发送用户名"anonymous" 和任意密码来完成(注意,有些服务器需要e-mail地址来代替密码):
client.login("anonymous", "ftp4j");
使用远程FTP服务来做任何事情,然后再断开连接:
client.disconnect(true);
这会向远程器发送FTP QUIT命令, 以进行一个合法断开流程.如果你只是想中断连接而不想向服务器发送任何通知,那么可以使用:
client.disconnect(false);
使用代理进行连接
客户端通过连接器(一个继承自it.sauronsoftware.ftp4j.FTPConnector的对象)来连接服务器, 它将返回一个已经打开的连接(一个实现了it.sauronsoftware.ftp4j.FTPConnection 接口的对象).这也是为什么ftp4j 可以支持大量代理的原因.
在连接远程服务器前,客户端实例可以使用setConnector() 方法来设置连接器:
client.setConnector(anyConnectorYouWant);
如果没有设置连接器的话,会使用默认的连接器DirectConnector (it.sauronsoftware.ftp4j.connectors.DirectConnector), 它实现了对远程服务器的直接连接,且不会使用代理。
如果你只能通过代理来连接远程服务器, ftp4j包可以让你在下面的连接器中进行选择:
- HTTPTunnelConnector (it.sauronsoftware.ftp4j.connectors.HTTPTunnelConnector)
它可以通过HTTP代理来进行连接,并支持CONNECT方法. - FTPProxyConnector (it.sauronsoftware.ftp4j.connectors.FTPProxyConnector)
它可以通过FTP代理进行连接,支持SITE和OPEN命令风格的苛刻远程主机连接.其它类型的FTP代理,需要username@remotehost 认证,且可以不使用连接器,因为它们对于客户端来说是透明的。 - SOCKS4Connector (it.sauronsoftware.ftp4j.connectors.SOCKS4Connector)
它可以通过SOCKS 4/4a代理进行连接. - SOCKS5Connector (it.sauronsoftware.ftp4j.connectors.SOCKS5Connector)
它可以通过SOCKS 5代理进行连接.
因为ftp4j的连接器架构设计为可插拔的,因此你可以继承FTPConnector 抽象类来构建自己的连接器.
FTPS/FTPES 安全连接
ftp4j包支持FTPS (隐式基于 TLS/SSL的FTP) 和FTPES (显示基于TLS/SSL的FTP).
setSecurity() 方法可用来打开这种特性:
client.setSecurity(FTPClient.SECURITY_FTPS); // 启用 FTPS
client.setSecurity(FTPClient.SECURITY_FTPES); // 启用 FTPES
两个方法都需要在连接远程服务器前调用.
如果安全协议设置成了SECURITY_FTPS, 则connect() 方法默认使用的端口为990.
默认情况下,客户端对象商讨SSL连接会使用javax.net.ssl.SSLSocketFactory.getDefault()作为其套接字工厂.可通过调用client.setSSLSocketFactory()方法来改变默认套接字工厂. 另外一种SSLSocketFactory, 可用来信任远程服务器颁发的证书(谨慎使用):
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); // ...
浏览远程站点
获取当前目录的的绝对路径(此目录是FTP服务器的home目录):
String dir = client.currentDirectory();
改变目录:
client.changeDirectory(newPath);
你可以使用绝对路径和相对路径:
client.changeDirectory("/an/absolute/one");
client.changeDirectory("relative");
回到父目录:
client.changeDirectoryUp();
重命名文件和目录
要重命名远程文件或目录:
client.rename("oldname", "newname");
移动文件和文件家
rename() 方法也可以用来从当前位置移动文件或目录到其它位置.
在这个例子子,假设在当前工作目录中,你有一个名为"myfile.txt"的文件,然后你想将其移动到子目录"myfolder"中:
client.rename("myfile.txt", "myfolder/myfile.txt");
删除文件
要删除远程文件,需要调用:
client.deleteFile(relativeOrAbsolutePath);
在这个例子中:
client.deleteFile("useless.txt");
创建、删除目录
如果远程服务给你机会的话,你可以在远程站点上创建新目录:
client.createDirectory("newfolder");
你也可以已存在的目录:
client.deleteDirectory(absoluteOrRelativePath);
在这个例子中:
client.deleteDirectory("oldfolder");
请注意,通常情况下,FTP 服务器只允许删除空目录.
列出文件、目录、连接
FTP 协议并不会提供大量支持方法来获取工作目录的完整信息.通常LIST命令会给你想知道的东西,但不辛的是,每个服务器会使用不同样式的响应. 这意味着某些服务器会返回UNIX样式的目录,有些服务器会返回DOS样式的目录,其它的服务器又会使用别的样式.
ftp4j 包可以处理许多的LIST响应格式, 并将它们构建成统一目录内容的结构对象表示.当前ftp4j可以处理:
- UNIX 样式及其变种(如MAC样式)
- DOS 样式
- NetWare 样式
- EPLF
- MLSD
这可以通过使用可插拔的parsers来完成.包it.sauronsoftware.ftp4j.listparsers包含了用于处理上述样式的对象.大多数时间,这些已经够用了。
要列出当前工作目录下的文件或文件夹,可调用:
FTPFile[] list = client.list();
如果你收到了FTPListParseException (it.sauronsoftware.ftp4j.FTPListParseException) 异常,这就意味着服务器对LIST命令返回了不可理解的样式,即它不是上述列出的样式.因此,你可以尝试使用listNames() 方法, 但它并不如list()方法有优势.。为了弥补这种缺陷,你可以构建你自己的LIST响应解析器,以支持你遇到的样式.你可以实现FTPListParser (it.sauronsoftware.ftp4j.FTPListParser) 接口,然后你可以在client的addListParser()方法使用此实现.
FTPFile (it.sauronsoftware.ftp4j.FTPFile) 对象提供了目录内容的表示,包括文件,子目录和连接. 根据服务器的响应,FTPFile对象的某些字段可以是null 的或者是无意义的.请检查javadocs来了解细节.
在list() 方法中你也可以使用文件过滤参数,如:
FTPFile[] list = client.list("*.jpg");
如果连接服务器明确支持MLSD命令, ftp4j会用其来代替基本的LIST命令。MLSD的响应事实更为标准,准确,更易解析.不幸的是,不是所有服务器都支持这个命令,并且有些服务器支持得非常糟糕.基于这些理由,开发者可以控制ftp4j是否应该使用MLSD命令,即通过调用FTPClient对象的setMLSDPolicy()方法. 合法的值:
FTPClient.MLSD_IF_SUPPORTED
client只在服务器明确支持MLSD命令时,才使用MLSD命令. 这是ftp4j默认的行为.
FTPClient.MLSD_ALWAYS
client总是会使用MLSD命令, 即便服务器没有明确表明支持MLSD命令.
FTPClient.MLSD_NEVER
client绝不使用MLSD命令,即便服务器明确表明支持MLSD命令.
例如:
client.setMLSDPolicy(FTPClient.MLSD_NEVER);
获取文件、目录的最后修改时间
通常情况下FTPFile对象会告诉你条目的最后修改时间, 但正如上面描述的,这依赖于服务器发回的响应.如果你需要最后的修改时间,但你又不能通过list()方法得到,那么可以尝试这样做:
java.util.Date md = client.modifiedDate("filename.ext");
下载、上传文件
下载远程文件最简单的方式是调用download(String, File) 方法:
client.download("remoteFile.ext", new java.io.File("localFile.ext"));
要上传:
client.upload(new java.io.File("localFile.ext"));
要在已有文件中上传追加内容:
client.append(new java.io.File("localFile.ext"));
这些是阻塞式调用:它们会在传输完成后(或failed, 或 aborted时)才返回. 此外同步锁是否由客户端来实施的,因为在每个时间段内只允许有一个常规的FTP通信.在每个时间段内,你可以处理多个传输器,即使用多个FTPClient 对象,每个都与服务器建立一个私有连接.
你可以使用FTPDataTransferListener (it.sauronsoftware.ftp4j.FTPDataTransferListener)对象来监控传输.你可以自己实现一个:
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
}
}
现在像下面这样来下载或上传:
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());
当client处理下载或上传时,传输器可以被同一个FTPClient对象的不同线程通过调用 abortCurrentDataTransfer() 方法aborted. 此方法还需要一个boolean参数:true表示执行合法的abort过程(即向服务器发送ABOR命令), false表示实然关闭传输器,而不向服务器发送通知:
client.abortCurrentDataTransfer(true); // Sends ABOR
client.abortCurrentDataTransfer(false); // Breaks abruptly
需要注意的是,list()和listNames() 方法暗中包含了数据传输器,因abortCurrentDataTransfer() 方法也可以用来中断其list过程.
当数据传输器在download(), upload(), append(), list() and listNames() 方法中中断时,将会抛出FTPAbortedException (it.sauronsoftware.ftp4j.FTPAbortedException).
下载和上传操作可通过restartAt 参数来重新恢复:
client.download("remoteFile.ext", new java.io.File("localFile.ext"), 1056);
此操作会文件的第1056个字节处继续执行下载操作。第一个传输的字节将是第1057个.
其它 download(), upload() 和append()方法的变种可以让你使用流来替代java.io.File对象.因此你可以在数据库,网络连接或其它流上来传输数据。
Active 、 passive 数据传输模式
客户端和服务器之间的数据传输通道是通过单独的网络连接来建立的. 在传输通道建立期间,服务器可以是active或passive的. 服务器激活数据传输时,工作如下:
- client向服务器发送其IP地址和端口号.
- client向服务器发送数据传输请求,并在之前发送的端口上启动监听.
- 服务器使用客户端提供的地址和端口来连接客房端.
- 数据传输将在新建立的通道中进行.
active模式需要你的client能够收到来自服务器的连接.如果你的client处于防火墙, 代理或这两者混合之后,那么大部分时间都会出现问题,因为它不能收到来外界的连接. 下面是passive数据传输模式:
- client要求服务器准备好一个passive数据传输.
- 服务器使用其IP地址和端口号进行响应.
- client请求传输和连接.
- 数据传输将在新建立的通道中进行.
在passive模式中,客户端连接不要求能收到服务器的连接请求.
在ftp4j中,你可以使用下面的调用来切换active、passive模式:
client.setPassive(false); // Active mode
client.setPassive(true); // Passive mode
ftp4j client passive 标志的默认值为true: 如果你没有调用setPassive(false) ,你的客户端在每次传输前,都会向服务器请求passive模式.
当使用 passive文件传输时,服务器会提供一个 IP地址和一个端口号.作为FTP规范的client,需要使用给定的主机号和端口进行连接.在商业环境中,这种行为可能会经常带来问题,因为NAT配置可能会阻止对IP地址的连接.这就是为什么FTP clients通常会忽略服务器返回的任何IP地址,进而在通信线路中使用同样的主机来连接服务器.ftp4j的行为依赖于服务器因素:
- 每个FTPConnector 都有其默认行为.大部分连接器都会忽略服务器返回的IP地址。目前,默认使用返回地址的连接器是FTPProxyConnector.
- 连接器的行为可通过定义名为ftp4j.passiveDataTransfer.useSuggestedAddress的系统属性来覆盖。如果设置为"true", "yes" 或"1",所有连接器都会使用服务器返回的地址,反之,如果将其设置为"false", "no" or "0", 所有服务器都不会使用返回的地址.
- 最后,连接器的默认行为和全局设置都可以在任何连接器实例中进行覆盖。你可通过获取客房端连接器,并调用其setUseSuggestedAddressForDataConnections() 方法来达到目的.
在active传输模式中,可以设置下面的系统属性:
ftp4j.activeDataTransfer.hostAddress
主机地址.当服务器请求执行与客户端连接时,client会跳转到给定地址的服务器. 此值应该是一个有效的IPv4地址,如:178.12.34.167. 如果没有提供此值,客户端会自动解析系统地址.但如果client运行于LAN中,为了激活数据传输,将会使用带端口转发的路由器来连接外部服务器,那么自动探测到的地址可能不是正确的. 当系统有多个网络接口时,也可能发生这种情况.通常使用系统属性,可以解决这种问题
ftp4j.activeDataTransfer.portRange
连接端口范围. client会在其中挑选一个来进行数据传输.此值 必须是start-stop 形式 ,如6000-7000 表示client只会在给定范围内挑选一个端口来连接服务器.默认情况下没有指定端口范围:这表示client会挑选任何一个可用的端口.
ftp4j.activeDataTransfer.acceptTimeout
以毫秒为单位的连接超时时间. 如果服务器不能在给定超时时间内连接client,传输会因FTPDataTransferException异常而中断.0值表示永不超时。默认值30000 (即30秒).
要设置系统属性,你可以:
用一个或多个 -Dproperty=value参数来启动JVM.如:
java -Dftp4j.activeDataTransfer.hostAddress=178.12.34.167 -Dftp4j.activeDataTransfer.portRange=6000-7000 -Dftp4j.activeDataTransfer.acceptTimeout=5000 MyClass
直接在代码中设置系统属性,如:
System.setProperty("ftp4j.activeDataTransfer.hostAddress", "178.12.34.167");
System.setProperty("ftp4j.activeDataTransfer.portRange", "6000-7000");
System.setProperty("ftp4j.activeDataTransfer.acceptTimeout", "5000");
二进制和文本数据传输类型
数据传输的另一个核心概念是binary 和textual 类型.当传传输的文件是二进制文件时,它将视为二进制流,服务器会按原样存储。而文本数据传输会将传输的文件视为字符流,会进行字符集转换. 假设你的client正运行Windows平台上,而服务器运行UNIX上,它们的默认字符集通常是不同的. client以文本类型来发送文件时,client会假设文件是按机器标准字符集来编码的,因此在发送前,它会解码每个字符并将其编码为 中间字符集. 服务器收到流后,在存储前,会解码中间字符集,并将其编码为机器默认的字符集.字节虽然被改变了,但内容是相同的。
你可以调用下面的方法选择你传输的类型:
client.setType(FTPClient.TYPE_TEXTUAL);
client.setType(FTPClient.TYPE_BINARY);
client.setType(FTPClient.TYPE_AUTO);
默认的TYPE_AUTO常量 ,会让client自动来挑选类型:如果文件的扩展名是client能被识别的文本类型标记,那么它会选择文本传输器来执行. 文件扩展名是通过FTPTextualExtensionRecognizer (it.sauronsoftware.ftp4j.FTPTextualExtensionRecognizer) 实例来识别的. 默认扩展识别器是it.sauronsoftware.ftp4j.recognizers.DefaultTextualExtensionRecognizer, 会将下面的视为文本类型:
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
你可通过实现FTPTextualExtensionRecognizer 接口来实现你自己的识别器,但你可以更喜欢使用 class ParametricTextualExtensionRecognizer(it.sauronsoftware.ftp4j.recognizers.ParametricTextualExtensionRecognizer)便利类.
无论如何,都不要忘记将你的识别器设置在client中:
client.setTextualExtensionRecognizer(myRecognizer);
数据传输压缩
有些服务器支持数据传输压缩特性-MODE Z. 在传输大文件时,此特性可以节省带宽.一旦client连上服务器并通过认证,就可通过调用下面的方法来检查是否支持压缩:
boolean compressionSupported = client.isCompressionSupported();
如果服务器端支持压缩,就可通过下面的调用来启用压缩:
client.setCompressionEnabled(true);
在此调用之后,后续的数据传输(下载,上传,列举操作)都被将压缩以节省带宽.
数据传输压缩可通过下面的调用来禁用:
client.setCompressionEnabled(false);
也可以检查标记值:
boolean compressionEnabled = client.isCompressionEnabled();
请注意:压缩数据传输只当压缩支持且启用了的情况下才会发生.
默认情况下,压缩是禁用的,即便是服务器支持压缩. 如果有需要,可以显示地打开.
不做任何事(NOOPing the server)
假设你的client什么事情都不做,因为在等待用户输入. 通常情况下, FTP服务器会自动断开非活跃客户端. 为了避免超时,你可以发送NOOP命令.
此命令不会做任何事情,但它会向服务器说明:客户端仍然还活着,请重围超时计数器.调用如下:
client.noop();
当非活跃超时发生时,客户端也可以自动发送NOOPs. 默认情况下,此特性是禁用的。它可以在 setAutoNoopTimeout() 方法中设置超时时间时启用,并提供一个毫秒为单位的值.如:
client.setAutoNoopTimeout(30000);
使用此值,client会在30秒后发送一个NOOP命令.
NOOP超时可通过设置小于等于0的值来禁用:
client.setAutoNoopTimeout(0);
网站特殊的自定义命令
你可以像下面这样来发送站点特殊命令:
FTPReply reply = client.sendSiteCommand("YOUR COMMAND");
你也可以发送自定义命令:
FTPReply reply = client.sendCustomCommand("YOUR COMMAND");
sendSiteCommand() 和 sendCustomCommand() 都会返回一个FTPReply (it.sauronsoftware.ftp4j.FTPReply)对象.使用此对象,你可以检查服务器的响应代码和消息.
FTPCodes (it.sauronsoftware.ftp4j.FTPCodes) 接口报告了一些通用的FTP响应代码,因此你可以使用这些包中的某个来进行匹配.
异常处理
ftp4j 包定义了五种类型的异常:
- FTPException (it.sauronsoftware.ftp4.FTPException)
依赖于方法,这会报告抛出了一个FTP故障.你可以检查报告的错误码,使用FTPCodes 常量来获取故障原因的详细信息. - FTPIllegalReplyException (it.sauronsoftware.ftp4.FTPIllegalReplyException)
这表示远程服务器使用非法方式进行了应答, 这与FTP是不兼容的. 这应该是非常罕见的. - FTPListParseException (it.sauronsoftware.ftp4.FTPListParseException)
这通常发生在list()方法,如果服务器发回的响应不能被客户端包中现有解析器进行的话,就会抛出此种异常 - FTPDataTransferException (it.sauronsoftware.ftp4.FTPDataTransferException)
当数据传输 (download, upload, but also list and listNames) 因网络连接错误失败时,就会抛出此种异常. - FTPAbortedException (it.sauronsoftware.ftp4.FTPAbortedException)
当数据传输 (download, upload, but also list and listNames) 因客户端发出中断请求失败时,抛出的异常.