问题描述:
项目要求从FTP服务器根据上传的路径批量获取附件存储到本地,这里我用的是org.apache.commons.net.ftp.FTPClient来实现ftp的连接,我定义了一个FtpConnection类用来包装初始化FTPClient、下载文件和删除文件的方法。
public class FtpConnection {
private FTPClient ftpClient = new FTPClient();
public FTPClient init(FtpProperties properties) {
try {
log.info("connecting...ftp服务器[{}]", properties.getHost() + ":" + properties.getPort());
ftpClient.connect(properties.getHost(), properties.getPort());
ftpClient.login(properties.getUsername(), properties.getPassword());
int replyCode = ftpClient.getReplyCode(); //是否成功登录服务器
if (!FTPReply.isPositiveCompletion(replyCode)) {
ftpClient.disconnect();
System.out.println("connect failed...");
}
if (replyCode == 1) {
ftpClient.enterLocalActiveMode();//主动模式
} else if (replyCode == 2) {
ftpClient.enterLocalPassiveMode(); //被动模式
}
ftpClient.setControlEncoding("GBK");//中文支持
ftpClient.sendCommand("OPTS UTF8", "ON");
log.info("connect successful...ftp serer:{}:{}", properties.getHost(), properties.getPort());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return ftpClient;
}
...
}
然后通过对路径的遍历下载文件
for (String filePath : filePaths) {
int index = 0;
//判断路径下的文件是否存在
if (!connection.existFile(filePath)) {
log.info("路径:{}文件不存在", filePath);
continue;
}
//根据文件路径获取文件名称
String fileName = getFileName(filePath);
//根据ftp的文件路径获取文件流
InputStream inputStream = connection.downloadFile(filePath);
if (inputStream != null) {
//执行业务逻辑
}
index++;
}
但是通过这种方式永远都只能下载到第一次遍历的文件,第二个路径的文件就会提示不存在,于是我就在每一次遍历的时候都把connection执行init()一次,这样虽然可以拿到文件,但是这种重复的连接ftp服务器应该会有一些隐患在里面。在后来看其它博主的博客后才找到问题原因:
for (String filePath : filePaths) {
int index = 0;
//判断路径下的文件是否存在
if (!connection.existFile(filePath)) {
log.info("路径:{}文件不存在", filePath);
continue;
}
//根据文件路径获取文件名称
String fileName = getFileName(filePath);
//根据ftp的文件路径获取文件流
InputStream inputStream = connection.downloadFile(filePath);
if (inputStream != null) {
//执行业务逻辑
}
/*
* 官方说法是:完成文件传输必须调用completePendingCommand和检查它的返回值
* 来验证成功。如果没有这样做,后续命令可能会意外出错
* 简单来说:completePendingCommand()会一直等FTP Server返回226
* Transfer complate,但是FTP Server只有在接收InputStream执行close()方法的时候,
* 才会返回。所以要先执行close()方法。
*/
inputStream.close();
connection.getFtpClient().completePendingCommand();
index++;
}
我的理解是,在获取流执行完业务流程后需要关闭流并且执行completePendingCommand()方法,FTP服务器才会判断这一次获取流的动作算是完成,如果没有这个完成动作,下一次获取流就会一直读到空的InputStream,所以就会判断文件不存在。