Java中apache下面FTPClient主动模式和被动模式

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了一段
  1. 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.
  2. So it is recommended to switch to local passive mode before transferring data, by invoking the methodenterLocalPassiveMode() of the FTPClient class.</span>
     这句话,讲的很准确。FTPClient连接FTP服务的时候,Java中org.apache.commons.net.ftp.FTPClient默认使用的应该是主动模式。所谓主动模式:就是指客户端连接服务端的时候,告诉服务端,我们之间要进行通讯,数据交换。我申请开辟一个端口,专门用于我们之间的通信,也即C(client)端主动向S(Server)端发起的请求。被动模式就是指,一开始服务一起来,S端变开启一个端口告诉C端,我们之间的通讯就在这个端口下。也就C端被动的接受服务端。
        理清了这两种模式之后,感觉还是无从下手。这个时候由于对网络的那一块不熟悉,基本上判断不了主动模式和被动模式,因此,只能求助于我们公司负责网络运维的同事。经过他们的帮忙,从而确认了,对方使用的FTP模式为被动模式。再回到代码中,看到以前的FTP工具类
  1. /**
  2. * 上传文件(主动模式)--默认的模式
  3. * @param ip
  4. * @param port
  5. * @param userName
  6. * @param password
  7. * @param sourceStream
  8. * @param ftpDir
  9. * @param ftpFileName
  10. * @param charset
  11. * @throws FrontEndException
  12. */
  13. public final static void upload(String ip, int port, String userName, String password, InputStream sourceStream, String ftpDir, String ftpFileName, String charset) throws FrontEndException {
  14. FTPClient ftpClient = new FTPClient();
  15.  
  16. try {
  17. if (port <= 0)
  18. ftpClient.connect(ip);
  19. else ftpClient.connect(ip, port);
  20. ftpClient.login(userName, password);
  21. //设置上传目录
  22. ftpClient.changeWorkingDirectory(ftpDir);
  23. ftpClient.setBufferSize(1024);
  24. ftpClient.setControlEncoding(charset);
  25. //设置文件类型(二进制)
  26. ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
  27. ftpClient.storeFile(ftpFileName, sourceStream);
  28. } catch (IOException e) {
  29. throw new FrontEndException("上传文件失败", e);
  30. } finally {
  31. try {
  32. ftpClient.disconnect();
  33. } catch (IOException e) {
  34. LOGGER.error("断开ftp连接异常", e);
  35. }
  36. }
  37. }
心想,或许,真的是和这个模式有关。此时我再仔细Debug了一下。当执行到ftpClient.storeFile(ftpFileName, sourceStream);我看到返回的是false,那这个时候基本上可以断定是文件没有上传成功。如果服务器端使用的是被动模式,而我们使用主动模式去上传文件的话。服务器肯定不会接收的
这个时候,感觉问题应该差不多找到了。网上一搜,加上一句设置被动模式的代码
//设置被动模式  ftpClient.enterLocalPassiveMode();
再仔细测试了一下,果然问题就出在这里。解决了。此时正确的代码为
  1. /**
  2. * 被动模式---上传文件
  3. * @param ip
  4. * @param port
  5. * @param userName
  6. * @param password
  7. * @param sourceStream
  8. * @param ftpDir
  9. * @param ftpFileName
  10. * @param charset
  11. * @throws FrontEndException
  12. */
  13. public final static void uploadPassiveMode(String ip, int port, String userName, String password, InputStream sourceStream, String ftpDir, String ftpFileName, String charset) throws FrontEndException {
  14. FTPClient ftpClient = new FTPClient();
  15.  
  16. try {
  17. if (port <= 0)
  18. ftpClient.connect(ip);
  19. else ftpClient.connect(ip, port);
  20. //登录
  21. boolean login = ftpClient.login(userName, password);
  22. StringBuilder service = new StringBuilder();
  23. service.append("上传FTP【ip="+ip+",port="+port+"】,");
  24. if(login){
  25. LOGGER.info(service.append("登录成功...").toString());
  26. //设置上传目录
  27. ftpClient.changeWorkingDirectory(ftpDir);
  28. ftpClient.setBufferSize(1024);
  29. ftpClient.setControlEncoding(charset);
  30. //设置被动模式
  31. <span style="color:#ff0000;">ftpClient.enterLocalPassiveMode();</span>
  32. //设置文件类型(二进制)
  33. ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
  34. if(ftpClient.storeFile(ftpFileName, sourceStream)){
  35. LOGGER.info(service.append("上传成功...").toString());
  36. } else {
  37. LOGGER.info(service.append("上传失败...").toString());
  38. }
  39. } else {
  40. LOGGER.info(service.append("登录失败...").toString());
  41. }
  42. } catch (IOException e) {
  43. throw new FrontEndException("上传文件失败", e);
  44. } finally {
  45. try {
  46. ftpClient.disconnect();
  47. } catch (IOException e) {
  48. LOGGER.error("断开ftp连接异常", e);
  49. }
  50. }
  51. }
同理,我们在下载对应的结果文件的时候。也需要设置相应的被动模式。
======================附上FTP上传和下载文件时候的步骤===========================
  1. To properly write code to upload files to a FTP server using Apache Commons Net API, the following steps should be followed:
  2. Connect and login to the server.
  3. Enter local passive mode for data connection.
  4. Set file type to be transferred to binary.
  5. Create an InputStream for the local file.
  6. Construct path of the remote file on the server. The path can be absolute or relative to the current working directory.
  7. Call one of the storeXXX()methods to begin file transfer. There are two scenarios:
  8. 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.
  9. 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.
  10. Close the opened InputStream and OutputStream.
  11. Call completePendingCommand() method to complete transaction.
  12. Logout and disconnect from the server.
    1.  
    2. <span style="color:#ff0000;">NOTES: we should check return value of the storeXXX() and completePendingCommand() method to ensure the upload is completed successfully.</span>

 

posted @ 2019-05-28 17:44  戈博折刀  阅读(1061)  评论(0编辑  收藏  举报