Java中apache下面FTPClient主动模式和被动模式
FTPClient主动模式和被动模式
昨日,碰到一个Java中关于使用org.apache.commons.net.ftp.FTPClient(commons-net-2.2.jar包)上传和下载文件的问题。以前,代码中有过FTP上传txt文件的工具类。心想,不用多说,直接上ip、port、username、password和相应的存放文件的目录,程序跑起来就是。开始,也没去仔细debug调试,心想程序没报错,也没怎么的,文件应该上传成功了。便告知对方通道去FTP服务器上拿文件,处理便是。结果,怎么都没有,,,额。。。。怎么可能。
中午吃完饭上来,再调试程序,再对比了一下。逻辑没区别啊,应该也没问题。可咋就是看不到文件。这时,我便用filezilla软件连接到对方FTP服务器上。开始,我怀疑是对方的FTP服务器指定的目录没有读写权限,登上去,手动上传了一个文件,并且成功了。这个时候,便可以排除没有读写权限。
那这又会是什么原因呢?这个时候,没辙,只能求助百度,google了。百度能搜索到的东西真的很少,而且讲的不全,但是这个时候,网上看到一些也碰到过这种情况的。说FTP服务器的模式有主动模式、被动模式两种。说的情况也和我碰到的类似。这个时候,心里就认准可能是和服务器设置的模式有关。google了一段
-
IMPORTANCES<span style="color:#009900;">: By default, the FTP protocol establishes a data connection by opening a port on the client and allows the server connecting to this port. This is called local active mode, but it is usually blocked by firewall so the file transfer may not work. Fortunately, the FTP protocol has another mode, local passive mode, in which a data connection is made by opening a port on the server for the client to connect – and this is not blocked by firewall.
-
So it is recommended to switch to local passive mode before transferring data, by invoking the methodenterLocalPassiveMode() of the FTPClient class.</span>
理清了这两种模式之后,感觉还是无从下手。这个时候由于对网络的那一块不熟悉,基本上判断不了主动模式和被动模式,因此,只能求助于我们公司负责网络运维的同事。经过他们的帮忙,从而确认了,对方使用的FTP模式为被动模式。再回到代码中,看到以前的FTP工具类
-
/**
-
* 上传文件(主动模式)--默认的模式
-
* @param ip
-
* @param port
-
* @param userName
-
* @param password
-
* @param sourceStream
-
* @param ftpDir
-
* @param ftpFileName
-
* @param charset
-
* @throws FrontEndException
-
*/
-
public final static void upload(String ip, int port, String userName, String password, InputStream sourceStream, String ftpDir, String ftpFileName, String charset) throws FrontEndException {
-
FTPClient ftpClient = new FTPClient();
-
-
try {
-
if (port <= 0)
-
ftpClient.connect(ip);
-
else ftpClient.connect(ip, port);
-
ftpClient.login(userName, password);
-
//设置上传目录
-
ftpClient.changeWorkingDirectory(ftpDir);
-
ftpClient.setBufferSize(1024);
-
ftpClient.setControlEncoding(charset);
-
//设置文件类型(二进制)
-
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
-
ftpClient.storeFile(ftpFileName, sourceStream);
-
} catch (IOException e) {
-
throw new FrontEndException("上传文件失败", e);
-
} finally {
-
try {
-
ftpClient.disconnect();
-
} catch (IOException e) {
-
LOGGER.error("断开ftp连接异常", e);
-
}
-
}
-
}
这个时候,感觉问题应该差不多找到了。网上一搜,加上一句设置被动模式的代码
//设置被动模式 ftpClient.enterLocalPassiveMode();
再仔细测试了一下,果然问题就出在这里。解决了。此时正确的代码为
-
/**
-
* 被动模式---上传文件
-
* @param ip
-
* @param port
-
* @param userName
-
* @param password
-
* @param sourceStream
-
* @param ftpDir
-
* @param ftpFileName
-
* @param charset
-
* @throws FrontEndException
-
*/
-
public final static void uploadPassiveMode(String ip, int port, String userName, String password, InputStream sourceStream, String ftpDir, String ftpFileName, String charset) throws FrontEndException {
-
FTPClient ftpClient = new FTPClient();
-
-
try {
-
if (port <= 0)
-
ftpClient.connect(ip);
-
else ftpClient.connect(ip, port);
-
//登录
-
boolean login = ftpClient.login(userName, password);
-
StringBuilder service = new StringBuilder();
-
service.append("上传FTP【ip="+ip+",port="+port+"】,");
-
if(login){
-
LOGGER.info(service.append("登录成功...").toString());
-
//设置上传目录
-
ftpClient.changeWorkingDirectory(ftpDir);
-
ftpClient.setBufferSize(1024);
-
ftpClient.setControlEncoding(charset);
-
//设置被动模式
-
<span style="color:#ff0000;">ftpClient.enterLocalPassiveMode();</span>
-
//设置文件类型(二进制)
-
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
-
if(ftpClient.storeFile(ftpFileName, sourceStream)){
-
LOGGER.info(service.append("上传成功...").toString());
-
} else {
-
LOGGER.info(service.append("上传失败...").toString());
-
}
-
} else {
-
LOGGER.info(service.append("登录失败...").toString());
-
}
-
} catch (IOException e) {
-
throw new FrontEndException("上传文件失败", e);
-
} finally {
-
try {
-
ftpClient.disconnect();
-
} catch (IOException e) {
-
LOGGER.error("断开ftp连接异常", e);
-
}
-
}
-
}
======================附上FTP上传和下载文件时候的步骤===========================
-
To properly write code to upload files to a FTP server using Apache Commons Net API, the following steps should be followed:
-
Connect and login to the server.
-
Enter local passive mode for data connection.
-
Set file type to be transferred to binary.
-
Create an InputStream for the local file.
-
Construct path of the remote file on the server. The path can be absolute or relative to the current working directory.
-
Call one of the storeXXX()methods to begin file transfer. There are two scenarios:
-
Using an InputStream-based approach: this is the simplest way, since we let the system does the ins and outs. There is no additional code, just passing the InputStream object into the appropriate method, such as storeFile(String remote, InputStream local) method.
-
Using an OutputStream-based approach: this is more complex way, but more control. Typically we have to write some code that reads bytes from the InputStream of the local file and writes those bytes into the OutputStream which is returned by the storeXXX() method, such as storeFileStream(String remote) method.
-
Close the opened InputStream and OutputStream.
-
Call completePendingCommand() method to complete transaction.
-
Logout and disconnect from the server.
-
-
<span style="color:#ff0000;">NOTES: we should check return value of the storeXXX() and completePendingCommand() method to ensure the upload is completed successfully.</span>