加強的Java FTP工具----擴展免費版的edtftpj
edtftpjs是國外的一個公司所作。有免費版、企業版之分,還有不用語言的版本。商業版的功能強大,是很是優秀的FTP組建。免費的湊合能用,可是功能相對簡單,實現粗糙。使用起來問題多多。
爲了讓免費版的edtftpj工具也具備商業版的一些強勁功能,本人利用業餘時間作了擴展,通過測試能夠正常使用。其中的算法也許不是最好,也歡迎高人提供更好的算法。
擴展的主要圍繞最經常使用的功能來進行:
一、加強上傳下載功能,使其支持文件和文件夾,若是是文件夾,上傳下載保持源的目錄結構。
二、加強判斷文件、文件夾是否存在的方法,使得ftp的文件操做如本地文件File操做同樣容易。
三、添加判斷是否爲文件、是否爲目錄的方法。
四、增長FTP配置管理的工具實現,這個不是主要的,就不貼了。
環境:
Java SE 1.5
edtftpjs-2.03 free版
實現思路:
繼承FTP客戶端核心的類com.enterprisedt.net.ftp.FileTransferClient,添加一些更爲通用的有效的方法。自己考慮到覆蓋,感受不妥。就擴展吧!
實現代碼:
import com.enterprisedt.net.ftp.FTPException;
import com.enterprisedt.net.ftp.FileTransferClient;
import com.enterprisedt.net.ftp.WriteMode;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import zzvcom.cms.ccm.commons.StringTookit;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
/**
* FTP加強工具
*
* @author leizhimin 2008-12-13 16:13:01
*/
public
class UltraFTPClient
extends FileTransferClient {
private
static Log log = LogFactory.getLog(UltraFTPClient.
class);
public UltraFTPClient() {
}
/**
* 下載文件(夾),在本地保持FTP上的目錄結構
*
* @param localFolderPath 本地存放文件夾
* @param remotePath 遠程文件(夾)路徑
* @param remoteSubPath 遠程文件存放相對根目錄
* @throws FTPException
* @throws IOException
*/
public
void ftpDownload(
final String localFolderPath,
final String remotePath, String remoteSubPath)
throws FTPException, IOException, ParseException {
if (isDir(remoteSubPath)) {
String localPath = localFolderPath + StringTookit.getRelativeRootPath(remoteSubPath, StringTookit.getParentPath(remotePath));
if (!
new File(localPath).exists())
new File(localPath).mkdirs();
String[] x = directoryNameList(remoteSubPath,
false);
for (String fname : x) {
String rmFilePath = StringTookit.formatPath(remoteSubPath +
"/" + fname);
if (isDir(rmFilePath)) {
ftpDownload(localFolderPath, remotePath, rmFilePath);
}
else {
String _localPath = localFolderPath +
"/" +
StringTookit.getRelativeRootPath(rmFilePath, StringTookit.getParentPath(remotePath));
downloadFile(_localPath, rmFilePath, WriteMode.OVERWRITE);
}
}
}
else
if (isFile(remotePath)) {
String localPath = localFolderPath + StringTookit.getRelativeRootPath(remoteSubPath, remotePath);
downloadFile(localPath, remoteSubPath, WriteMode.OVERWRITE);
}
else {
log.error(
"所下載的文件或文件夾不存在,請檢查!");
}
log.info(
"FTP下載從服務器上的" + remoteSubPath +
"下載到本地" + localFolderPath +
"結束!");
}
/**
* 上傳文件(夾),在FTP上保持本地的目錄結構
*
* @param localFile 本地文件
* @param localFilePath 本地文件的路徑
* @param remoteSubPath 遠程文件存放相對根目錄
* @throws IOException
* @throws FTPException
*/
public
void ftpUpload(File localFile,
final String localFilePath,
final String remoteSubPath)
throws IOException, FTPException {
if (localFile.isDirectory()) {
for (File file : localFile.listFiles()) {
if (file.isDirectory()) {
String remotePath = StringTookit.formatPath(remoteSubPath) + StringTookit.getRelativeRootPath(file.getPath(), StringTookit.getParentPath(localFilePath));
log.info(remotePath);
if (!isExist(remotePath)) createDirectory(remotePath);
ftpUpload(file, localFilePath, remoteSubPath);
}
else {
String remotePath = StringTookit.formatPath(remoteSubPath) +
StringTookit.getRelativeRootPath(file.getPath(), StringTookit.getParentPath(localFilePath));
uploadFile(file.getPath(), remotePath, WriteMode.APPEND);
}
}
}
else
if (localFile.isFile()) {
String remotePath = StringTookit.formatPath(remoteSubPath) +
StringTookit.getRelativeRootPath(localFile.getPath(), StringTookit.getParentPath(localFilePath));
if (!isExist(StringTookit.getParentPath(remotePath)))
createDirectory(StringTookit.getParentPath(remotePath));
System.out.println(remotePath);
uploadFile(localFile.getPath(), remotePath, WriteMode.APPEND);
}
log.info(
"FTP上傳" + localFile.getPath() +
"到" + remoteSubPath +
"目錄下結束!");
}
/**
* @param remotePath 遠程文件(夾)路徑
* @return 遠程的文件(夾)資源是否存在
*/
public
boolean isExist(String remotePath) {
boolean flag =
true;
try {
directoryList(remotePath);
}
catch (Exception e) {
flag =
false;
}
return flag;
}
/**
* @param remotePath 遠程文件(夾)路徑
* @return 當遠程資源存在且爲文件時返回ture,不然返回false
*/
public
boolean isFile(String remotePath) {
try {
int size = directoryList(remotePath).length;
if (size >= 0) {
if (exists(remotePath)) {
return
true;
}
}
}
catch (Exception e) {
}
return
false;
}
/**
* @param remotePath 遠程文件(夾)路徑
* @return 當遠程資源存在且爲文件夾時返回ture,不然返回false
*/
public
boolean isDir(String remotePath) {
try {
int size = directoryList(remotePath).length;
if (size >= 0) {
if (exists(remotePath)) {
return
false;
}
else {
return
true;
}
}
}
catch (Exception e) {
}
return
false;
}
}
爲了支持目錄運算與不一樣操做系統的兼容性,寫了一個文件路徑處理工具。有了這一整套的工具後,上面的工做才能更清晰的去作。
/**
* 字符串工具箱
*
* @author leizhimin 2008-12-15 22:40:12
*/
public
final
class StringTookit {
/**
* 將一個字符串的首字母改成大寫或者小寫
*
* @param srcString 源字符串
* @param flag 大小寫標識,ture小寫,false大些
* @return 改寫後的新字符串
*/
public
static String toLowerCaseInitial(String srcString,
boolean flag) {
StringBuilder sb =
new StringBuilder();
if (flag) {
sb.append(Character.toLowerCase(srcString.charAt(0)));
}
else {
sb.append(Character.toUpperCase(srcString.charAt(0)));
}
sb.append(srcString.substring(1));
return sb.toString();
}
/**
* 將一個字符串按照句點(.)分隔,返回最後一段
*
* @param clazzName 源字符串
* @return 句點(.)分隔後的最後一段字符串
*/
public
static String getLastName(String clazzName) {
String[] ls = clazzName.split(
"\\.");
return ls[ls.length - 1];
}
/**
* 格式化文件路徑,將其中不規範的分隔轉換爲標準的分隔符,而且去掉末尾的"/"符號。
*
* @param path 文件路徑
* @return 格式化後的文件路徑
*/
public
static String formatPath(String path) {
String reg =
"\\\\+|/+";
String temp = path.trim().replaceAll(reg,
"/");
if (temp.endsWith(
"/")) {
return temp.substring(0, temp.length() - 1);
}
return temp;
}
/**
* 獲取文件父路徑
*
* @param path 文件路徑
* @return 文件父路徑
*/
public
static String getParentPath(String path) {
return
new File(path).getParent();
}
/**
* 獲取相對路徑
*
* @param fullPath 全路徑
* @param rootPath 根路徑
* @return 相對根路徑的相對路徑
*/
public
static String getRelativeRootPath(String fullPath, String rootPath) {
String relativeRootPath =
null;
String _fullPath = formatPath(fullPath);
String _rootPath = formatPath(rootPath);
if (_fullPath.startsWith(_rootPath)) {
relativeRootPath = fullPath.substring(_rootPath.length());
}
else {
throw
new RuntimeException(
"要處理的兩個字符串沒有包含關係,處理失敗!");
}
if (relativeRootPath ==
null)
return
null;
else
return formatPath(relativeRootPath);
}
}
FTP客戶端配置工具:
一個好的工具,配置也很講究,這裏也不例外,通過精心處理,FTP服務器配置變得輕鬆自如:
import com.enterprisedt.net.ftp.FTPConnectMode;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import zzvcom.cms.ccm.commons.JavaXmlTookit;
import zzvcom.cms.ccm.commons.SysParamsTookit;
/**
* FTP配置
*
* @author leizhimin 2008-12-5 22:49:39
*/
public
class FtpServerConfigration {
private
static
final Log log = LogFactory.getLog(FtpServerConfigration.
class);
private String username;
//用戶名
private String password;
//密碼
private String ip;
//ip
private Integer port;
//端口
private Integer timeout;
//超時時間
private Integer buffersize;
//緩存大小
private Integer notifytime;
//通知時間
private String connectMode;
//鏈接模式
private String encoding;
//編碼方式
public FtpServerConfigration(String username, String password, String ip, Integer port) {
this.username = username;
this.password = password;
this.ip = ip;
this.port = port;
this.timeout = Integer.valueOf(SysParamsTookit.getProperty(
"timeout",
"36000000"));
this.buffersize = Integer.valueOf(SysParamsTookit.getProperty(
"buffersize",
"2048000"));
this.notifytime = Integer.valueOf(SysParamsTookit.getProperty(
"notifytime",
"5000"));
this.connectMode = SysParamsTookit.getProperty(
"connectMode",
"PASV");
this.encoding = SysParamsTookit.getProperty(
"encoding",
"GBK");
}
public FtpServerConfigration(String ftpConfigXml) {
FtpServerConfigration config = (FtpServerConfigration) JavaXmlTookit.xml2Java(ftpConfigXml, FtpServerConfigration.
class);
if (StringUtils.isBlank(config.getUsername())
|| StringUtils.isBlank(config.getPassword())
|| StringUtils.isBlank(config.getIp())
|| config.getPort() ==
null) {
log.error(
"FTP最基本的配置屬性(username、password、ip、port)不能爲空,請檢查!");
}
else {
this.username = config.getUsername();
this.password = config.getPassword();
this.ip = config.getIp();
this.port = config.getPort();
}
if (config.getTimeout() ==
null)
this.timeout = Integer.valueOf(SysParamsTookit.getProperty(
"timeout",
"36000000"));
if (config.getBuffersize() ==
null)
this.buffersize = Integer.valueOf(SysParamsTookit.getProperty(
"buffersize",
"2048000"));
if (config.getNotifytime() ==
null)
this.notifytime = Integer.valueOf(SysParamsTookit.getProperty(
"notifytime",
"5000"));
if (StringUtils.isBlank(config.getConnectMode()))
this.connectMode = SysParamsTookit.getProperty(
"connectMode",
"PASV");
if (StringUtils.isBlank(config.getEncoding()))
this.encoding = SysParamsTookit.getProperty(
"encoding",
"GBK");
}
/**
* 獲取當前FTP鏈接配置
*
* @return 當前FTP鏈接配置
*/
public FtpServerConfigration getConfigration() {
return
this;
}
/**
* 構建FTP客戶端鏈接,並進行鏈接
*
* @return FTP客戶端鏈接
* @throws Exception 當構建客戶端失敗時拋出
*/
public UltraFTPClient buildFtpClient()
throws Exception {
UltraFTPClient client =
new UltraFTPClient();
try {
client.setUserName(username);
client.setPassword(password);
client.setRemoteHost(ip);
client.setRemotePort(port);
client.setTimeout(timeout);
client.getAdvancedSettings().setTransferBufferSize(buffersize);
client.getAdvancedSettings().setTransferNotifyInterval(notifytime);
client.getAdvancedSettings().setControlEncoding(encoding);
// client.setEventListener(
new UploadListener(client));
//設置事件監聽器
if (connectMode.equalsIgnoreCase(
"ACTIVE")) {
client.getAdvancedFTPSettings().setConnectMode(FTPConnectMode.ACTIVE);
//設置爲被動模式
}
else
if (connectMode.equalsIgnoreCase(
"PASV")) {
client.getAdvancedFTPSettings().setConnectMode(FTPConnectMode.PASV);
//設置爲被動模式
}
else {
log.error(
"標識爲" + connectMode +
"的FTP鏈接模式配置錯誤,鏈接模式僅有兩種ACTIVE和PASV,請檢查!");
}
client.connect();
log.info(
"FTP鏈接成功!詳細信息(遠程主機:" + ip +
",用戶名:" + username +
")");
}
catch (Exception e) {
log.info(
"FTP建立鏈接發生異常!", e);
throw e;
}
return client;
}
public String getUsername() {
return username;
}
public
void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public
void setPassword(String password) {
this.password = password;
}
public String getIp() {
return ip;
}
public
void setIp(String ip) {
this.ip = ip;
}
public Integer getPort() {
return port;
}
public
void setPort(Integer port) {
this.port = port;
}
public Integer getTimeout() {
return timeout;
}
public
void setTimeout(Integer timeout) {
this.timeout = timeout;
}
public Integer getBuffersize() {
return buffersize;
}
public
void setBuffersize(Integer buffersize) {
this.buffersize = buffersize;
}
public Integer getNotifytime() {
return notifytime;
}
public
void setNotifytime(Integer notifytime) {
this.notifytime = notifytime;
}
public String getConnectMode() {
return connectMode;
}
public
void setConnectMode(String connectMode) {
this.connectMode = connectMode;
}
public String getEncoding() {
return encoding;
}
public
void setEncoding(String encoding) {
this.encoding = encoding;
}
}
系統默認的FtP參數配置:
### FTP默認配置參數 ###
# FTP鏈接模式:ACTIVE,PASV爲兩種鏈接模式
#port=21
ftp.timeout=360000
ftp.buffersize=20480
ftp.notifytime=5000
ftp.connectMode=PASV
ftp.encoding=GBK
進行測試:
這裏只給出測試大概過程:
一、建立一個FTP配置對象,並從FTP配置對象構建一個加強的FTP客戶端對象。
UltraFTPClient client = new FtpServerConfigration("testuser", "123456", "192.168.0.2", 21).buildFtpClient();
二、根據有了客戶端後,就能夠調用加強的和原有的任何方法,來完成你想要的FTP操做。
三、操做完成後關閉FTP鏈接。
client.disconnect();
遺留問題:
FTP的寫模式在下載的時候沒法指定,指定爲WriteMode.APPEND(追加)是最理想的,可是如今沒法作到,只要設置就出錯。也許是由於我服務器配置的問題,緣由不明。
若是您解決了遺留問題,或者發現了新的問題,也請留言告訴我。
題外話:
下一步本人將利用業餘時間使用Apache的Commons Net提供的基礎API來實現這個FTP客戶端。
一我的的精力和能力都是有限的,但願這個FTP客戶端未來能成爲一個頗受歡迎的開源組件,任何人均可以輕鬆使用其構建本身的FTP應用。