要求
要運行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) 因客戶端發出中斷請求失敗時,拋出的異常.