本文主要介绍Apache Commons Net对于FTP文件上传下载的实现,这里不推荐使用JDK自带的实现方式,尽管他们的使用方式类似。
实例
package com.spbp.util;
import org.apache.commons.net.ftp.FTPClient;
import java.io.*;
/**
* 类名:FtpUtil
* 作者:spbp
* 日期:2018/1/9 9:00
* 说明:
*/
public class FtpUtil {
private static FTPClient ftpClient;
private static final int DEFAULT_PORT=21;
private static final int TIME_OUT=90;
/**
* 连接ftp服务端
* @param hostname 主机名
* @param port 端口号
* @param username 账户
* @param password 密码
* @param dir 工作目录
* @return
*/
public static boolean connect(String hostname,int port,String username,String password,String dir){
boolean flag=false;
try {
if(port<0||port>65535){
port=FtpUtil.DEFAULT_PORT;
}
//1.创建客户端
ftpClient=new FTPClient();
//2.设置参数(超时时间,编码格式,工作模式等)
//数据连接的超时时间,0表示超时无限制
// ftpClient.setDataTimeout(0);
//文件上传下载时,命令连接发送心跳数据的超时时间
ftpClient.setControlKeepAliveTimeout(FtpUtil.TIME_OUT);
//设置编码格式
// ftpClient.setAutodetectUTF8(true);
ftpClient.setControlEncoding("GBK");
// 设置为被动模式
ftpClient.enterLocalPassiveMode();
//3.连接
ftpClient.connect(hostname,port);
//4.登录
flag=ftpClient.login(username,password);
// 设置文件类型,必须在login后
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
//5.设置工作目录
if(dir!=null&&!"".equals(dir)&&flag){
ftpClient.changeWorkingDirectory(dir);
}
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
/**
* ftp断开连接
*/
public static void close(){
try {
ftpClient.logout();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(ftpClient.isConnected()){
ftpClient.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* ftp文件上传 服务器文件已存在会覆盖,可以在storeFile前加入校验
* @param srcFile 源文件位置
* @param dir ftp服务器工作目录
* @param fileName 服务器文件名称
* @return
*/
public static boolean upload(String srcFile,String dir,String fileName){
FileInputStream fis=null;
boolean flag=false;
try {
//1.切换工作目录
if(dir!=null&&!"".equals(dir)){
ftpClient.changeWorkingDirectory(dir);
}
//2.获取本地文件
File file=new File(srcFile);
//3.创建文件流
fis=new FileInputStream(file);
//4.上传文件
flag=ftpClient.storeFile(fileName,fis);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fis!=null){
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return flag;
}
/**
* ftp文件下载 文件不存在时,下载的是空文件,在retrieveFile前加入校验
* @param srcFile 下载位置
* @param dir ftp服务器工作目录
* @param fileName 服务器文件
* @return
*/
public static boolean download(String srcFile,String dir,String fileName){
FileOutputStream fos=null;
boolean flag=false;
try {
//1.切换工作目录
if(dir!=null&&!"".equals(dir)){
ftpClient.changeWorkingDirectory(dir);
}
//服务器文件不存在校验
// if(ftpClient.mlistFile(fileName)==null){
// throw new FileNotFoundException("文件不存在");
// }
//2.获取本地文件
File file=new File(srcFile);
//3.创建文件流
fos=new FileOutputStream(file);
//4.下载文件
flag=ftpClient.retrieveFile(fileName,fos);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fos!=null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return flag;
}
/**
* 1.
* 创建服务器连接耗时:75毫秒
* 文件下载耗时:102551毫秒
* 文件上传耗时:79782毫秒
* 2.
* 创建服务器连接耗时:86毫秒
* 文件下载耗时:113232毫秒
* 文件上传耗时:91327毫秒
* 3.
* 创建服务器连接耗时:109毫秒
* 文件下载耗时:102782毫秒
* 文件上传耗时:91349毫秒
* @param args
*/
public static void main(String[] args) {
long start=System.currentTimeMillis();
FtpUtil.connect("127.0.0.1",21,"ftpadmin","ftp123456",null);
long connect=System.currentTimeMillis();
System.out.println("创建服务器连接耗时:"+(connect-start)+"毫秒");
FtpUtil.download("a.rmvb","/FTPDownload","YYSS.rmvb");
long down=System.currentTimeMillis();
System.out.println("文件下载耗时:"+(down-connect)+"毫秒");
FtpUtil.upload("a.rmvb","/FTPUpload","YYSS1.rmvb");
long up=System.currentTimeMillis();
System.out.println("文件上传耗时:"+(up-down)+"毫秒");
FtpUtil.close();
}
}
这里的例子是参考官方API给出的简单例子,应该在开发的过程中根据业务需求做出适当的调整。
结束语
- 业务场景:
通过定时任务,定时读取FTP服务器的文件,并进行具体的业务处理。 - 问题:
程序运行一段时间后,经常出现程序卡死,也没有抛出异常信息。 - 解决方案:
首先排查定位问题,在网上查了好多资料(Apache FTPClient操作“卡死”问题的分析和解决),排除定时任务程序引起问题的可能,最终定位到:ftp操作的连接模式引起的此次程序卡死。
在解决这个问题的过程中对ftp文件上传下载的基本操作进行学习。通过对ftp协议实现原理的学习,了解为什么会引起这个问题(主动模式被动模式的区别)。