加强的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应用。