博客统计信息

51cto博客之星
用户名:leizhimin
文章数:583
评论数:1875
访问量:5112655
无忧币:14589
博客积分:15350
博客等级:10
注册日期:2006-11-01

基于ftp4j的FTP客户端工具
2009-11-30 16:51:34
版权声明:原创作品,如需转载,请与作者联系。否则将追究法律责任。
基于ftp4j的FTP客户端工具

ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能。可以将ftp4j嵌到你的Java应用中,来传输文件(包括上传和下载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括:通过TCP/IP直接连接,通过FTP代理、HTTP代理、SOCKS4/4a代理和SOCKS5代理连接,通过SSL安全连接。

ftp4j这是一个基本类库,用起来有些不爽,首先是受检查异常太多太多,这是合理的,把异常留给使用者灵活处理,其次是提供的客户单API太基础,还不够强悍。下面是我针对实际中最常用的功能所作的一个工具类。

在对待异常的方式上,将检查异常全转换为运行时异常,并对一些潜在操作的错误进行检查,提供了原API中没有的一些功能,批量下载、任务侦听器、检查FTP上文件或目录是否存在以及类型。
 
下面是实现代码:
package lavasoft.common.ftp;

import it.sauronsoftware.ftp4j.FTPClient;
import it.sauronsoftware.ftp4j.FTPFile;
import lavasoft.common.PathToolkit;

import java.io.File;
import java.util.List;

/**
* TTP客户端工具
*
* @author leizhimin 2009-11-30 10:20:17
*/

public final class FTPToolkit {

        private FTPToolkit() {
        }

        /**
         * 创建FTP连接
         *
         * @param host         主机名或IP
         * @param port         ftp端口
         * @param username ftp用户名
         * @param password ftp密码
         * @return 一个客户端
         */

        public static FTPClient makeFtpConnection(String host, int port, String username, String password) {
                FTPClient client = new FTPClient();
                try {
                        client.connect(host, port);
                        client.login(username, password);
                } catch (Exception e) {
                        throw new FTPRuntimeException(e);
                }
                return client;
        }

        /**
         * FTP下载文件到本地一个文件夹,如果本地文件夹不存在,则创建必要的目录结构
         *
         * @param client                    FTP客户端
         * @param remoteFileName    FTP文件
         * @param localFolderPath 存的本地目录
         */

        public static void download(FTPClient client, String remoteFileName, String localFolderPath) {
                int x = isExist(client, remoteFileName);
                MyFtpListener listener = MyFtpListener.instance(FTPOptType.UP);
                File localFolder = new File(localFolderPath);
                if (localFolder.isFile()) {
                        throw new FTPRuntimeException("所要的下载保存的地方是一个文件,无法保存!");
                } else {
                        if (!localFolder.exists())
                                localFolder.mkdirs();
                }
                if (x == FTPFile.TYPE_FILE) {
                        String localfilepath = PathToolkit.formatPath4File(localFolderPath + File.separator + new File(remoteFileName).getName());
                        try {
                                if (listener != null)
                                        client.download(remoteFileName, new File(localfilepath), listener);
                                else
                                        client.download(remoteFileName, new File(localfilepath));
                        } catch (Exception e) {
                                throw new FTPRuntimeException(e);
                        }
                } else {
                        throw new FTPRuntimeException("所要下载的文件" + remoteFileName + "不存在!");
                }
        }

        /**
         * FTP上传本地文件到FTP的一个目录下
         *
         * @param client                     FTP客户端
         * @param localfile                本地文件
         * @param remoteFolderPath FTP上传目录
         */

        public static void upload(FTPClient client, File localfile, String remoteFolderPath) {
                remoteFolderPath = PathToolkit.formatPath4FTP(remoteFolderPath);
                MyFtpListener listener = MyFtpListener.instance(FTPOptType.UP);
                try {
                        client.changeDirectory(remoteFolderPath);
                        if (!localfile.exists()) throw new FTPRuntimeException("所要上传的FTP文件" + localfile.getPath() + "不存在!");
                        if (!localfile.isFile()) throw new FTPRuntimeException("所要上传的FTP文件" + localfile.getPath() + "是一个文件夹!");
                        if (listener != null)
                                client.upload(localfile, listener);
                        else
                                client.upload(localfile);
                        client.changeDirectory("/");
                } catch (Exception e) {
                        throw new FTPRuntimeException(e);
                }
        }

        /**
         * FTP上传本地文件到FTP的一个目录下
         *
         * @param client                     FTP客户端
         * @param localfilepath        本地文件路径
         * @param remoteFolderPath FTP上传目录
         */

        public static void upload(FTPClient client, String localfilepath, String remoteFolderPath) {
                File localfile = new File(localfilepath);
                upload(client, localfile, remoteFolderPath);
        }

        /**
         * 批量上传本地文件到FTP指定目录上
         *
         * @param client                     FTP客户端
         * @param localFilePaths     本地文件路径列表
         * @param remoteFolderPath FTP上传目录
         */

        public static void uploadListPath(FTPClient client, List<String> localFilePaths, String remoteFolderPath) {
                remoteFolderPath = PathToolkit.formatPath4FTP(remoteFolderPath);
                try {
                        client.changeDirectory(remoteFolderPath);
                        MyFtpListener listener = MyFtpListener.instance(FTPOptType.UP);
                        for (String path : localFilePaths) {
                                File file = new File(path);
                                if (!file.exists()) throw new FTPRuntimeException("所要上传的FTP文件" + path + "不存在!");
                                if (!file.isFile()) throw new FTPRuntimeException("所要上传的FTP文件" + path + "是一个文件夹!");
                                if (listener != null)
                                        client.upload(file, listener);
                                else
                                        client.upload(file);
                        }
                        client.changeDirectory("/");
                } catch (Exception e) {
                        throw new FTPRuntimeException(e);
                }
        }

        /**
         * 批量上传本地文件到FTP指定目录上
         *
         * @param client                     FTP客户端
         * @param localFiles             本地文件列表
         * @param remoteFolderPath FTP上传目录
         */

        public static void uploadListFile(FTPClient client, List<File> localFiles, String remoteFolderPath) {
                try {
                        client.changeDirectory(remoteFolderPath);
                        remoteFolderPath = PathToolkit.formatPath4FTP(remoteFolderPath);
                        MyFtpListener listener = MyFtpListener.instance(FTPOptType.UP);
                        for (File file : localFiles) {
                                if (!file.exists()) throw new FTPRuntimeException("所要上传的FTP文件" + file.getPath() + "不存在!");
                                if (!file.isFile()) throw new FTPRuntimeException("所要上传的FTP文件" + file.getPath() + "是一个文件夹!");
                                if (listener != null)
                                        client.upload(file, listener);
                                else
                                        client.upload(file);
                        }
                        client.changeDirectory("/");
                } catch (Exception e) {
                        throw new FTPRuntimeException(e);
                }
        }


        /**
         * 判断一个FTP路径是否存在,如果存在返回类型(FTPFile.TYPE_DIRECTORY=1、FTPFile.TYPE_FILE=0、FTPFile.TYPE_LINK=2)
         * 如果文件不存在,则返回一个-1
         *
         * @param client         FTP客户端
         * @param remotePath FTP文件或文件夹路径
         * @return 存在时候返回类型值(文件0,文件夹1,连接2),不存在则返回-1
         */

        public static int isExist(FTPClient client, String remotePath) {
                remotePath = PathToolkit.formatPath4FTP(remotePath);
                int x = -1;
                FTPFile[] list = null;
                try {
                        list = client.list(remotePath);
                } catch (Exception e) {
                        return -1;
                }
                if (list.length > 1) return FTPFile.TYPE_DIRECTORY;
                else if (list.length == 1) {
                        FTPFile f = list[0];
                        if (f.getType() == FTPFile.TYPE_DIRECTORY) return FTPFile.TYPE_DIRECTORY;
                        //假设推理判断
                        String _path = remotePath + "/" + f.getName();
                        try {
                                int y = client.list(_path).length;
                                if (y == 1) return FTPFile.TYPE_DIRECTORY;
                                else return FTPFile.TYPE_FILE;
                        } catch (Exception e) {
                                return FTPFile.TYPE_FILE;
                        }
                } else {
                        try {
                                client.changeDirectory(remotePath);
                                return FTPFile.TYPE_DIRECTORY;
                        } catch (Exception e) {
                                return -1;
                        }
                }
        }

        /**
         * 关闭FTP连接,关闭时候像服务器发送一条关闭命令
         *
         * @param client FTP客户端
         * @return 关闭成功,或者链接已断开,或者链接为null时候返回true,通过两次关闭都失败时候返回false
         */


        public static boolean closeConnection(FTPClient client) {
                if (client == null) return true;
                if (client.isConnected()) {
                        try {
                                client.disconnect(true);
                                return true;
                        } catch (Exception e) {
                                try {
                                        client.disconnect(false);
                                } catch (Exception e1) {
                                        e1.printStackTrace();
                                        return false;
                                }
                        }
                }
                return true;
        }
}
 
package lavasoft.common;

import java.io.File;

/**
* 路径处理工具,操作系统自适应
*
* @author leizhimin 2009-11-30 16:01:34
*/

public final class PathToolkit {
        private PathToolkit() {
        }

        /**
         * 格式化文件路径,将其中不规范的分隔转换为标准的分隔符,并且去掉末尾的文件路径分隔符。
         * 本方法操作系统自适应
         *
         * @param path 文件路径
         * @return 格式化后的文件路径
         */

        public static String formatPath4File(String path) {
                String reg0 = "\\\\+";
                String reg = "\\\\+|/+";
                String temp = path.trim().replaceAll(reg0, "/");
                temp = temp.replaceAll(reg, "/");
                if (temp.length() > 1 && temp.endsWith("/")) {
                        temp = temp.substring(0, temp.length() - 1);
                }
                temp = temp.replace('/', File.separatorChar);
                return temp;
        }

        /**
         * 格式化文件路径,将其中不规范的分隔转换为标准的分隔符
         * 并且去掉末尾的"/"符号(适用于FTP远程文件路径或者Web资源的相对路径)。
         *
         * @param path 文件路径
         * @return 格式化后的文件路径
         */

        public static String formatPath4FTP(String path) {
                String reg0 = "\\\\+";
                String reg = "\\\\+|/+";
                String temp = path.trim().replaceAll(reg0, "/");
                temp = temp.replaceAll(reg, "/");
                if (temp.length() > 1 && temp.endsWith("/")) {
                        temp = temp.substring(0, temp.length() - 1);
                }
                return temp;
        }

        /**
         * 获取FTP路径的父路径,但不对路径有效性做检查
         *
         * @param path FTP路径
         * @return 父路径,如果没有父路径,则返回null
         */

        public static String genParentPath4FTP(String path) {
                String pp = new File(path).getParent();
                if (pp == null) return null;
                else return formatPath4FTP(pp);
        }
}
 
package lavasoft.common.ftp;

/**
* FTP操作类型
*
* @author leizhimin 2009-11-30 11:16:59
*/

public enum FTPOptType {
        UP("上传"),
        DOWN("下载"),
        LIST("浏览"),
        DELFILE("删除文件"),
        DELFOD("删除文件夹"),
        RENAME("上传");

        private String optname;

        FTPOptType(String optname) {
                this.optname = optname;
        }

        public String getOptname() {
                return optname;
        }
}
 
package lavasoft.common.ftp;

import it.sauronsoftware.ftp4j.FTPDataTransferListener;

/**
* FTP监听器,做了简单实现,可以使用commons logger替换System.out.println
*
* @author leizhimin 2009-11-30 11:05:33
*/

public class MyFtpListener implements FTPDataTransferListener {
        private FTPOptType optType;

        public static MyFtpListener instance(FTPOptType optType) {
                return new MyFtpListener(optType);
        }

        private MyFtpListener(FTPOptType optType) {
                this.optType = optType;
        }

        public void started() {
                System.out.println(optType.getOptname() + ":FTP启动喽。。。。。。");
        }

        public void transferred(int length) {
                System.out.println(optType.getOptname() + ":FTP传输喽。。。。。。");

        }

        public void completed() {
                System.out.println(optType.getOptname() + ":FTP完成喽。。。。。。");
        }

        public void aborted() {
                System.out.println(optType.getOptname() + ":FTP中止喽。。。。。。");
        }

        public void failed() {
                System.out.println(optType.getOptname() + ":FTP挂掉喽。。。。。。");
        }
}
 
package lavasoft.common.ftp;

/**
* FTP异常
*
* @author leizhimin 2009-11-30 10:28:03
*/

public class FTPRuntimeException extends RuntimeException {
        public FTPRuntimeException() {
                super();
        }

        public FTPRuntimeException(String message) {
                super(message);
        }

        public FTPRuntimeException(String message, Throwable cause) {
                super(message, cause);
        }

        public FTPRuntimeException(Throwable cause) {
                super(cause);
        }
}
 
package lavasoft;

import it.sauronsoftware.ftp4j.FTPClient;
import lavasoft.common.ftp.FTPToolkit;

/**
* 简单测试下
*
* @author leizhimin 2009-11-30 12:25:42
*/

public class Test {
        public static void main(String args[]) throws Exception {
                String ftpip = "192.168.104.113";
                int ftpport = 21;
                String ftpuser = "vcomkp.ftpadmin";
                String ftppswd = "ftp";

                FTPClient client = FTPToolkit.makeFtpConnection(ftpip, ftpport, ftpuser, ftppswd);
                FTPToolkit.upload(client, "C:\\Dynamicclrr4.zip", "/aaa/bbb/ccc");
                FTPToolkit.download(client, "/Dynamicclrr4.zip", "D:\\");
                FTPToolkit.closeConnection(client);
        }
}
 
上传:FTP启动喽。。。。。。
上传:FTP传输喽。。。。。。
上传:FTP传输喽。。。。。。
上传:FTP完成喽。。。。。。
上传:FTP启动喽。。。。。。
上传:FTP传输喽。。。。。。
上传:FTP传输喽。。。。。。
上传:FTP完成喽。。。。。。

Process finished with exit code 0
 
本程序在RedHat AS 5上使用vsftpd测试通过,一般来说windows下测试通过的ftp工具,一般在Linux下通不过,因为Windows没有太多权限控制。而Linux控制非常严格,常常因为没有权限而导致操作失败,这时候,要对Linux的FTP用户给读写的权限才能正确执行以上代码。

另外说明一点,在判断ftp服务器上一个文件是否存在的isExist方法,具有一定一个局限性,体现在两方面:一是对特定组件版本的依赖性,不同版本list等方法实现不同,对错误路径的处理也不同。二是,不同的ftp服务器对发送list命令反馈的结果不尽相同。
 
这也是为什么很多ftp组件工具不去实现isExist的方法。对使用本方法测试有异常不符合事实的情况下,需要根据特定的环境测试重新实现isExist方法即可。
 
这里我给出了一个测试:
我用的vsftp目录结构如下
/aaa
/aaa/bbb/ccc
/aaa/bbb/ccc/Dynamicclrr4.zip
 
调用方法测试:
                System.out.println(FTPToolkit.isExist(client, "/aaa/bbb/ccc"));                                    //目录
                System.out.println(FTPToolkit.isExist(client, "/aaa/bbb/ccc/Dynamicclrr4.zip")); //文件
                System.out.println(FTPToolkit.isExist(client, "/aaa/bbb/ccc/xxxxxx.zip"));             //不存在的文件
                System.out.println(FTPToolkit.isExist(client, "/aaa/bbb/pppppdd"));                            //不存在的目录
 
1
0
-1
-1

Process finished with exit code 0
 
可见,打印的结果与实际情况相符。

本文出自 “熔 岩” 博客,转载请与作者联系!

分享至
更多
一键收藏,随时查看,分享好友!
0人
了这篇文章
类别:Java开源技术圈()┆阅读()┆评论() ┆ 推送到技术圈返回首页

文章评论

 
2009-11-30 18:02:58
学习了

2009-12-02 18:31:02
public static int isExist(FTPClient client, String remotePath)
当ftp远程目录中只有一个文件时
如:
\aaa\
  -- 111.txt

isExist(FTPClient client, "\aaa")返回为0
博主回复:
2009-12-02 20:55:07
0表示文件,正确的;


    /**
      * 判断一个FTP路径是否存在,如果存在返回类型(FTPFile.TYPE_DIRECTORY=1、FTPFile.TYPE_FILE=0、FTPFile.TYPE_LINK=2)
      * 如果文件不存在,则返回一个-1
      *
      * @param client       FTP客户端
      * @param remotePath FTP文件或文件夹路径
      * @return 存在时候返回类型值(文件0,文件夹1,连接2),不存在则返回-1
      */

2009-12-03 19:55:20
isExist(FTPClient client, "\aaa")是目录
111.txt才是文件

2009-12-03 19:56:56
如果目录中存在中文目录
list方法也会报错

2009-12-03 21:26:13
我是博主,看看代码就知道了,所有可能的异常都被捕获了。把你的报错代码贴出来看看,也好交流下,我是怎么测均返回一个数字,不存在的都是-1.

还有,我看你写的目录不对, ftp路径分隔符是“/",不是"\",当然你写错了找不到就给你返回个-1。

2009-12-07 10:08:02
博主同志,您的isExist方法改过,所以现在是正常的。第二点,你的isExist方法中 try {
                    client.changeDirectory(remotePath);
                    return FTPFile.TYPE_DIRECTORY;
                } catch (Exception e) {
                    return -1;
                }
client.changeDirectory(remotePath); 会更改客户端目录,建议更改为:
try {
            String tempPath = client.currentDirectory();
                         client.changeDirectory(remotePath);
                         client.changeDirectory(tempPath);
            return FTPFile.TYPE_DIRECTORY;
          } catch (Exception e) {
                return FILE_NOT_EXITS;
          }
博主回复:
2009-12-07 11:06:56
这篇文章发送到博客上,当时做了简单的测试,后来过了不超过一天吧,修改了一些细节,其中有文件路径工具中的格式化方法,还有就是isExist()方法。估计你没注意吧。还有就是监听器的一点改动。

你提出了一个建议,changeDirectory()因为会改动当前默认目录,而做了一个改进,判断完成后将目录再改回去。其实这个要说感觉没必要,因为上传下载都明确的制定了要上传的路径(可以看看上面FTP工具的方法)。原来的当前路径不会对上传下载影响。当然添加上也可以。均不影响使用。

2009-12-07 12:58:32
对于存在中文的目录与中文文件的问题,ftp4j的client.list方法均会报异常,你有解决方案么?
博主回复:
2009-12-07 17:07:38
没测试过中文。你可以研究下,中文问题与通信双方都有关系。

2009-12-14 15:51:00
大哥,方法写的不错。
我想问一下,
ftp4j download()方法,
你 怎么获得用户将下载路径
博主回复:
2009-12-14 16:17:45
你说的下载路径是什么含义?客户端存放的路径还是服务端要下载目标文件的路径?

对于客户端路径,你自己要下载存放到什么地方,这是客户端自己决定的。
同理,你要下载什么文件,你连路径都不知道怎么去下载啊?

不明白你问什么.

2009-12-15 09:00:17
下载路径是指目标路径啊。就是文件将要存放的地址。

还有一个问题啊。
用ftp4j实现文件上传下载功能,在本机测试时好用,
把程序部署到应用服务器上时,再进行文件上传才发现报错:
java.io.FileNotFoundException:找不到文件路径。
如何才能实现从客户端直接进行文件上传到FTP服务器的功能呢?

博主有没有试过啊。
博主回复:
2009-12-15 22:54:44
不知所云,你上传文件的过程就是直接进行文件上传到FTP服务器。
发生java.io.FileNotFoundException异常是因为你的本地文件找不到,没啥好奇的,你要传一个不存在的文件,当然会抛这种异常了。

2009-12-16 11:49:40
看来是我表达有问题了。

tomcat 启动本机可以实现上传。
而别的机子通过网络访问布署后的项目,
上传时找不到上传机子的本地文件。

2009-12-16 15:19:52
我在本地ftp download一个文件,本地操作系统是windows,为什么显示download成功,没有任何异常,但是本地目录下没有任何文件生成呢?本地还要做什么配置马?

用了N台测试,有的可以成功,又得不行。。。。

请问你知道缺少什么配置嘛?
博主回复:
2009-12-16 15:25:21
你应该好好研究下FTP的原理以及配置等等。。。

否则你也用不好ftp工具包。

2009-12-16 15:45:17
看了很久,没有头绪阿。。。请问你遇到过嘛?怎么配置呢?

2009-12-25 09:32:58
博主,我上次问过你的那个问题,
能不能帮我解答一下。

博主写的ftp4j实现文件上传功能, 在本地机上可以实现上传功能,把程序部署到应用服务器上,通过网络上传调用上传功能,会报找不到文件路径路径错误。在后台看是已经获得了文件路径的。
为什么还会报:java.io.FileNotFoundException错误。
博主回复:
2009-12-25 11:41:13
你对本地、远程的概念还很模糊。
你也对服务器的概念没认识,你的叙述看不明白啥意思。
本机、服务器是什么关系,本机不能当服务器了?服务器不能是本机?
ftp的配置和操作系统环境、ftp服务器软件都有关系,对客户端而言,你只能将客户端运行的机器上的文件上传到ftpserver,出现java.io.FileNotFoundException错误,说明你所上传的文件找不到,那你要查看这个文件是否存在,所传递的参数是否正确等等。


2009-12-25 14:32:34
多谢博主提点,
让我明白自身知识的浅薄。
解决问题还得从本质上的认识才行。

2010-01-06 17:50:33
博主,你写的upload方法,上传中文文件时会报
I/O error in data transfer 。
上传中文文件时该怎么处理。

2010-06-10 17:16:29
博主,ftp4j是否支持多线程下载啊。

2010-06-14 15:45:17
我早点看到这篇文,就不会用commons.net.ftp辛辛苦苦的做了,支持博主!

2010-06-14 15:50:19
用commons.ftp没办法做到上传进度的监听,我用的另外一个ftpclient实例去读已上传的文件,但返回的始终是0,用ftp4j就舒服很多。

2011-04-26 11:11:35
文章写得很好,但是为什么没有deleteFile呢?

 

发表评论            

【技术门诊】专家解析:软考重点难点及应试技巧
昵  称:
登录  快速注册
验证码:

请点击后输入验证码博客过2级,无需填写验证码

内  容: