|
增强的Java FTP工具----扩展免费版的edtftpj
edtftpjs是国外的一个公司所做。有免费版、企业版之分,还有不用语言的版本。商业版的功能强大,是非常优秀的FTP组建。免费的凑合能用,但是功能相对简单,实现粗糙。使用起来问题多多。
为了让免费版的edtftpj工具也具有商业版的一些强劲功能,本人利用业余时间做了扩展,经过测试可以正常使用。其中的算法也许不是最好,也欢迎高人提供更好的算法。
扩展的主要围绕最常用的功能来进行:
1、增强上传下载功能,使其支持文件和文件夹,如果是文件夹,上传下载保持源的目录结构。
2、增强判断文件、文件夹是否存在的方法,使得ftp的文件操作如本地文件File操作一样容易。
3、添加判断是否为文件、是否为目录的方法。
4、增加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
进行测试:
这里只给出测试大概过程:
1、创建一个FTP配置对象,并从FTP配置对象构建一个增强的FTP客户端对象。
UltraFTPClient client = new FtpServerConfigration(\"testuser\", \"123456\", \"192.168.0.2\", 21).buildFtpClient();
2、根据有了客户端后,就可以调用增强的和原有的任何方法,来完成你想要的FTP操作。
3、操作完成后关闭FTP连接。
client.disconnect();
遗留问题:
FTP的写模式在下载的时候无法指定,指定为WriteMode.APPEND(追加)是最理想的,但是现在无法做到,只要设置就出错。也许是因为我服务器配置的问题,原因不明。
如果您解决了遗留问题,或者发现了新的问题,也请留言告诉我。
题外话:
下一步本人将利用业余时间使用Apache的Commons Net提供的基础API来实现这个FTP客户端。
一个人的精力和能力都是有限的,希望这个FTP客户端将来能成为一个颇受欢迎的开源组件,任何人都可以轻松使用其构建自己的FTP应用。
|