文档章节

用commons-net包写Ftp客户端下载(一)

DavidBao
 DavidBao
发布于 2014/12/16 22:28
字数 2385
阅读 61
收藏 1

由于需要客户需求,需要把Ftp上的所有文件下载到本地,包括目录和文件。看到文件数量的时候我就哭了。。

几万个文件,晕死。这个地方我遇到的几个困难我会一一说明。

下载commons-net包我就不多说了。。

.首先先写客户端下载的工具类,就是封装了关于客户端连接FTP,断开,查询文件,以及下载文件等方法。

这里我借鉴了网上我忘了具体名字的里面的一些代码,所以有雷同请大家不要介意。。

 

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;
public class FtpHelper {
	// 日志
	private static Logger logger = Logger.getLogger(FtpHelper.class);
	// 客户端操作
	public FTPClient ftpClient = new FTPClient();

(这是类的开头,嘿嘿个人习惯。。不喜欢上传文件)

 

这里最主要的就是一个FTPClient 类,他就是客户端进行Ftp连接的关键类。

1.连接FTP服务器方法:这里面有个ConfigInfo就是取配置文件的信息,一会我会放出来。

/**
	 * 连接FTP服务器
	 * 
	 * @param hostname 服务器名称
	 * @param port 端口
	 * @param user 用户名
	 * @param password 密码
	 * @return 是否连接上
	 */
	public boolean connect(String hostname, int port, String user,
			String password) {
		logger.info("进行FTP连接......");
		logger.info("hostname:" + hostname + "  port:" + port + " user" + user
				+ " password:" + password);
		try {
			// 连接服务器
			ftpClient.connect(hostname, port);
			// 设置传输编码
			ftpClient.setControlEncoding("UTF-8");
			
			
			// 设置客户端操作系统类型,为windows 其实就是"WINDOWS" 虽然没用到
			FTPClientConfig conf = new FTPClientConfig(ConfigInfo.getSystem());
			// 设置服务器端语言 中文 "zh"
			conf.setServerLanguageCode(ConfigInfo.getServerLanguageCode());

			// 判断服务器返回值,验证是否已经连接上
			if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
				// 验证用户名密码
				if (ftpClient.login(user, password)) {
					logger.info("已经连接到ftp......");
					return true;
				}
				logger.error("连接ftp的用户名或者密码错误......");
				// 取消连接
				disconnect();

			}
		} catch (SocketException e) {
			logger.error("连接不上ftp....", e);
			// e.printStackTrace();
		} catch (IOException e) {
			logger.error("出现io异常....", e);
			// e.printStackTrace();
		}
		return false;
	}

 

这里需要注意的就是一个被动模式的设置和一个传输编码的设置

2.关闭FTP连接

/**
	 * 关闭FTP连接
	 */
	public void disconnect() {
		logger.info("进入FTP连接关闭连接方法...");
		// 判断客户端是否连接上FTP
		if (ftpClient.isConnected()) {
			// 如果连接上FTP,关闭FTP连接
			try {
				logger.info("关闭ftp连接......");
				ftpClient.disconnect();
			} catch (IOException e) {
				logger.error("关闭ftp连接出现异常......", e);
				// e.printStackTrace();
			}
		}
	}

这个就是判断是否连接上,如果连接上断开连接。

3. 查询当前工做空间下的所有ftp文件 包括了目录

/**
	 * 查询当前工做空间下的所有ftp文件包括了目录
	 * 
	 * @return 文件数组
	 */
	public FTPFile[] getFilesList() {
		logger.info("进入查询ftp所有文件方法.....");
		try {
			FTPFile[] ftpFiles = ftpClient.listFiles();
			int num = 0;
			for (FTPFile ftpFile : ftpFiles) {
				if (!ftpFile.isFile()) {
					continue;
				}
				num++;
			}
			logger.info("进入查询上文件个数.." + num);
			logger.info("进入查询ftp所有文件方法结束.....");
			return ftpFiles;
		} catch (IOException e) {
			logger.error("查询ftp上文件失败...", e);
			return null;
		}

	}

 

4.变更工作目录.变更工作目录其实就是去下级目录。因为ftp连接上默认是在根目录上,所以如果你想访问根目录下其他目录

里面的内容,需要变更到那个目录。

/**
	 * 变更工作目录
	 * 
	 * @param remoteDir 变更到的工作目录
	 */
	public boolean changeDir(String remoteDir) {
		try {
			logger.info("变更工作目录为:" + remoteDir);
			ftpClient.changeWorkingDirectory(new String(remoteDir
					.getBytes("UTF-8"), "iso8859-1"));
			return true;
		} catch (IOException e) {
			logger.error("变更工作目录为" + remoteDir + "失败", e);
			return false;
		}
	}

这里需要注意一下remoteDir就是目录的名称,FTP一次只能变更到一个目录下,然后这里有个编码问题,

这里我不知道为啥我设置了传输是UTF-8的还需要转码,本来这里我没有转码的,但是之后的测试,出现了各种错误,

才发现这里出现了问题,需要一UTF-8解码,然后iso8859-1编码。。。(如果有达人解决这个问题,请联系我。。。)

 6.变更工作目录到其父目录

/**
	 * 变更工作目录到其父目录
	 * 
	 * @return 是否变更成功
	 */
	public boolean changeToParentDir() {
		try {
			logger.info("变更工作目录到父目录");
			return ftpClient.changeToParentDirectory();
		} catch (IOException e) {
			logger.error("变更工作目录到父目录出错", e);
			return false;
		}
	}

不多说,变更到上级目录。。。

 

 7.从服务器上下载特定文件,重头戏。。。

     /**
     * 从服务器上下载特定文件
     *
     * @param remote
     * @param local
     * @return
     */
    public Boolean downloadonefile(String remote, String local) {
        //System.out.println(ftpClient.isConnected());
        logger.info("开始下载.....");
        logger.info("远程文件:"+ remote +" 本地文件存放路径:"+ local);
 
        // 设置被动模式
        ftpClient.enterLocalPassiveMode();
        // 设置以二进制方式传输
        try{
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        }catch(IOException e) {
            logger.error("设置以二进制传输模式失败...", e);
        }
 
        // 检查FTP上是否存在文件
        FTPFile[] files =null;
        try{
            files = ftpClient.listFiles(newString(remote.getBytes("UTF-8"),"iso8859-1"));
            logger.info(files==null?"不存在":"存在"+files.length);
            logger.info("搜索出来文件名为:");
            for(FTPFile file:files){
                logger.info(file.getName());
            }
        }catch(IOException e) {
            logger.error("检查远程文件是否存在失败....", e);
        }
        if(files ==null|| files.length ==0) {
            logger.error("远程文件不存在");
            returnfalse;
        }
 
        longftp_file_size = files[0].getSize();
        logger.info("远程文件的大小:"+ ftp_file_size);
        File local_file =newFile(local);
        InputStream in =null;
        OutputStream out =null;
         
        //判断本地文件是否存在,如果存在判断是否需要断点续传
        if(local_file.exists()) {
            logger.info("本地文件存在,判断是否需要续传.....");
            longlocal_file_size = local_file.length();
            logger.info("本地文件大小:"+ local_file_size);
 
            // 判断本地文件大小是否大于远程文件大小
            if(local_file_size >= ftp_file_size) {
                logger.info("本地文件大于等于远程文件,不需要续传");
                returntrue;
            }
 
            // 进行断点续传
            logger.info("开始断点续传.....");
            ftpClient.setRestartOffset(local_file_size);
            try{
                 
                //根据文件名字得到输入留
                in = ftpClient.retrieveFileStream(newString(remote.getBytes("UTF-8"),"iso8859-1"));
                //建立输出流,设置成续传
                out =newFileOutputStream(local_file,true);
                byte[] b =newbyte[1024];
                //已下载的大小
                longdowland_size = local_file_size;
                intflag =0;
                longcount;
                if(((ftp_file_size - dowland_size) % b.length) ==0) {
                    count = ((ftp_file_size - dowland_size) / b.length);
                }else{
                    count = ((ftp_file_size - dowland_size) / b.length) +1;
                }
 
                while(true) {
                    intnum = in.read(b);
                    //System.out.println(num);
                    if(num == -1)
                        break;
                    out.write(b,0, num);
                    dowland_size += num;
                    flag++;
                    //打印下载进度
                    if(flag %1000==0) {
                        logger.info("下载进度为: + (dowland_size *100/ ftp_file_size) +"%");
                    }
                }
                if(count == flag) {
                    logger.info("下载进度为:100%");
                }
                in.close();
                out.close();
            }catch(UnsupportedEncodingException e) {
                logger.error("字符转换失败", e);
                returnfalse;
            }catch(FileNotFoundException e) {
                logger.error("未找到文件", e);
                return false;          
            }catch(IOException e) {
                logger.error("出现io异常,请检查网络", e);
                return false;         }
           }else{
 
            logger.info("本地文件不存在,此文件为新文件,开始下载.....");
            byte[] b =newbyte[1024];
 
            try{
                //得到输入输出流
                in = ftpClient.retrieveFileStream(newString(remote.getBytes("UTF-8"),"iso8859-1"));
                out =newFileOutputStream(local);
                 
                //已下载的大小
                longdowland_size =0;
                intflag =0;
                longcount;
                if((ftp_file_size % b.length) ==0) {
                    count = (ftp_file_size / b.length);
                }else{
                    count = (ftp_file_size / b.length) +1;
                }
                while(true) {
                    intnum = in.read(b);
                    if(num == -1)
                        break;
                    out.write(b,0, num);
                    dowland_size += num;
                    flag++;
                    //打印下载进度
                    if(flag %1000==0) {
                        logger.info("下载进度为:"+ (dowland_size *100/ ftp_file_size) +"%");
                    }
                    // ftp_file_size
                }
                if(count == flag) {
                    logger.info("下载进度为:100%");
                }
                //关闭输入输出流
                in.close();
                out.close();
            }catch(UnsupportedEncodingException e) {
                logger.error("字符转换失败", e);
                return false;           
            }catch(IOException e) {
                logger.error("出现io异常请检查网络", e);
                return false;
            }
        }
        return true;
    }

这里借鉴了一下别人的代码,如有雷同,请不要介意啦。。

 

这里遇到的问题

a.编码解码,在ftpClient.listFiles(new String(remote.getBytes("UTF-8"), "iso8859-1"));

验证文件是否在服务器上存在的时候,需要转码。

同理得到输入流的时候:

ftpClient.retrieveFileStream(new String(remote.getBytes("UTF-8"), "iso8859-1"));

 也需要转码。

b.断点续传,其实就是看某个文件如果服务器存在之后,如果本地存在就判断,本地文件和服务器文件的大小。

如果本地大于等于服务器,就不需要。。。其实大于这种情况咋产生滴,是服务器那边的事情。。

(有可能这里会有人说不合理,大于的情况就说明变化了,应该重新传,但是我们这里客户的需求是服务器端文件,只会做增量操作,不会修改删除。所以。。。。当然,大家可以根据自己的情况进行变更)

如果小于,就从本地文件大小的位置开始续传,ftpClient.setRestartOffset(local_file_size);这个方法可以设置

输入流开始的位置。之后就是传输问题了。

c.由于本来用户是要求有个比例的,但是后来取消了,因为文件数量太大了。。。所以这里就是装饰了。。。

到此工具类写完了。哦对了,还有个东西

}

这样就齐了。。

 然后就是配置文件和ConfigInfo类,就是自己写的一个读取配置文件的类。

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.apache.log4j.Logger;


/**
 * 
 * 配置文件读取类
 * @author houly
 *
 */

//这个配制成 文件更改时间
public class ConfigInfo {
	/**FTP服务器地址或名称*/
	private String ftpHostName;
	/**FTP服务器ftp服务端口*/
	private int port;
	/**FTP服务器登陆用户名*/
	private String username;
	/**FTP服务器登陆密码*/
	private String password;
	/**FTP下载到本地路径*/
	private String ftpDownLoadDir;
	/**FTPserver操作系统*/
	private String system;
	/**FTP语言*/
	private String serverlanguagecode;
	/**FTP多线程下载线程数量*/
	private int threadNUM;
	private final String _URL = "/config.properties";
	//日志
	Logger logger = Logger.getLogger(ConfigInfo.class);

	private static ConfigInfo config = new ConfigInfo();

	public static String getFtpHostName() {
		return config.ftpHostName;
	}
	public static int getPort() {
		return config.port;
	}
	public static String getUsername(){
		return config.username;
	}
	public static String getPassword(){
		return config.password;
	}
	public static String getFtpDownLoadDir(){
		return config.ftpDownLoadDir;
	}

	
	public static String getSystem(){
		return config.system;
	}
	
	public static String getServerLanguageCode(){
		return config.serverlanguagecode;
	}
	
	public static int getThreadNUM(){
		return config.threadNUM;
	}
	private ConfigInfo() {
		loadConfig();
	}

	private void loadConfig() {
		InputStream is = this.getClass().getResourceAsStream(_URL);
		Properties pro = new Properties();
		try {
			pro.load(is);
		} catch (IOException e) {
			logger.error("config.properties配置文件加载错误", e);
		}
		ftpHostName = pro.getProperty("ftphostname");
		port = Integer.valueOf(pro.getProperty("port"));
		username=pro.getProperty("username");
		password=pro.getProperty("password");
		ftpDownLoadDir=pro.getProperty("ftpdownloaddir");
		system = pro.getProperty("system");
		serverlanguagecode = pro.getProperty("serverlanguagecode");
		threadNUM = Integer.valueOf(pro.getProperty("threadNUM"));
		
		logger.info("配置文件信息.....");
		logger.info("ftpHostName:"+ftpHostName);
		logger.info("port:"+port);
		logger.info("username:"+username);
		logger.info("password:"+password);
		logger.info("ftpDownLoadDir:"+ftpDownLoadDir);
		logger.info("system:"+system);
		logger.info("serverlanguagecode:"+serverlanguagecode);
		logger.info("threadNUM:"+threadNUM);
	}
}

配置文件 config.properties文件

ftphostname=localhost
port=21
username=admin
password=admin
ftpdownloaddir=d\:/360
system=WINDOWS
#system=UNIX
serverlanguagecode=zh
threadNUM=30

其他的就没了,然后这里有个线程数,是ftp客户端进行多线程下载的时候配置的。

© 著作权归作者所有

DavidBao
粉丝 114
博文 213
码字总数 126729
作品 0
昌平
私信 提问
加载中

评论(0)

用commons-net包写Ftp客户端下载(一)

由于需要客户需求,需要把Ftp上的所有文件下载到本地,包括目录和文件。看到文件数量的时候我就哭了。。 几万个文件,晕死。这个地方我遇到的几个困难我会一一说明。 下载commons-net包我就不...

猴子
2011/10/20
4.4K
1
用commons-net包写Ftp客户端下载(三)

由于上次说了,如果客户端遍历FTP服务器的话,如果FTP上文件数量很大,那样的话,递归遍历的时间会超级长。。 (还没有说明。。我们这是EJB项目。。是FTP服务器上调用我的这个客户端进行下载...

猴子
2011/10/20
507
1
FTP断点续传卡死的问题。

用Apache Commons-Net包中的FtpClient做文件上传,从手机上传到服务器,手机使用WiFi连接,Server是Linux的vsftpd搭建的。 有以下特殊情况:上传文件到一半的时候,手动关掉WiFi致其上传失败...

摆渡者
2015/07/29
1.9K
0
用commons-net包写Ftp客户端下载(二)

上次写完了工具类,那么然后就是另一个工具类了。 1.也就是对ftp上文件的遍历。 这里我发现了一个问题,下载文件的时候,每次都得重新连接,也就是一次连接只能下载一个文件。 虽然到最后我也...

猴子
2011/10/20
895
0
使用apache的FTPServer搭建ftp服务器

一:启动ftp服务 1.下载Apache FtpServer 1.0.6 Release: http://mina.apache.org/downloads-ftpserver.html 2.解压后res/conf下找到user.properties (修改密码为admin默认是md5加密后的) ......

路叫兽
2016/08/11
478
0

没有更多内容

加载失败,请刷新页面

加载更多

Java高级面试攻略:消息+缓存+高并发+高可用+架构+分布式+微服务

Java高级面试攻略:消息+缓存+高并发+高可用+架构+分布式+微服务,对每一个专题每一道列举出来的高频面试题,都会做一个面试官的心理分析以及题目的具体剖析,教你如何破解,攻克面试官! 攻...

眉目清俊
15分钟前
19
0
JavaScript 数组遍历方法的对比

前言 JavaScript 发展至今已经发展出多种数组的循环遍历的方法,不同的遍历方法运行起来那个比较快,不同循环方法使用在那些场景,下面将进行比较: 各种数组遍历的方法 for 语句 代码: var arr...

osc_fscujk71
23分钟前
5
0
Nginx和Tomcat配置https

Nginx配置htpps http{ #http节点中可以添加多个server节点 server{ #监听443端口 listen 443; #对应的域名,把baofeidyz.com改成你们自己的域名就可以了 ...

chinahufei
23分钟前
5
0
react调用方法

React里调用方法常见的方式有两种 (1)触发时通过回调函数触发 <Button type="primary" onClick={()=>this.updateBar()}>更新</Button> (2)直接通过方法名触发 <Button type="primary" o......

osc_vpogdtu8
24分钟前
26
0
Centos通过代理上网

阿里云ECS不能访问内外,购买NAT的除外,通过windows主机代理上网: 1.windows上安装代理工具,工具种类比较多,我这里使用的是privoxy 下载地址:http://www.privoxy.org/ 百度云盘(备用)...

osc_r94nrknb
26分钟前
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部