Apache FTPClient操作“卡死”问题的分析和解决

来源:互联网 发布:淘宝资生堂水之印 编辑:程序博客网 时间:2024/04/28 15:12

    最近在和一个第三方的合作中不得已需要使用FTP文件接口。由于FTP Server由对方提供,而且双方背后各自的网络环境都很不单纯等等原因,造成测试环境无法模拟实际情况。测试环境中程序一切正常,但是在部署到生产环境之后发现FTP操作不规律性出现“卡死”现象:程序捕获不到任何异常一直卡着,导致轮巡无法正常工作(由于担心在轮巡时间间隔内处理不能完成,我没有采用类似quartz或者crontab的定时任务,而是采用while-true然后sleep的方式)。

    为了解决这个问题,我首先考虑的是对于FTPClient的使用上没有设置超时时间,于是设置了ConnectTimeout、DataTimeout、DefaultTimeout后在生产环境上继续观察,但是问题依旧没有解决。后来我有些怀疑FTPClient api本身是不是有什么问题,想实在不行自己实现一个超时机制吧,不过还是不甘心,还是想从FTPClient api本身去解决问题。又经过一翻研究之后发现:需要使用被动模式,以下摘抄别人的一段简单描述:
在项目中使用commons-net-3.0.1.jar实现FTP文件的下载,在windows xp上运行正常,但是放到linux上,却出现问题,程序运行到 FTPClient.listFiles()或者FTPClient.retrieveFile()方法时,就停止在那里,什么反应都没有,出现假死状态。google一把,发现很多人也出现了此类问题,最终在一个帖子里找到了解决办法。在调用这两个方法之前,调用FTPClient.enterLocalPassiveMode();这个方法的意思就是每次数据连接之前,ftp client告诉ftp server开通一个端口来传输数据。为什么要这样做呢,因为ftp server可能每次开启不同的端口来传输数据,但是在linux上,由于安全限制,可能某些端口没有开启,所以就出现阻塞。OK,问题解决。

    于是我回滚了之前的修改,改为被动模式(关于FTP主动/被动模式的解释,这里我不多说了,关注的朋友可以自己查阅)。但是问题依旧。于是能想到的就是最有的绝招:实在不行自己实现一个超时机制吧。经过一翻研究最简单的方式就是使用:Future解决:

复制代码
 1 public static void main(String[] args) throws InterruptedException, ExecutionException { 2         final ExecutorService exec = Executors.newFixedThreadPool(1); 3  4         Callable<String> call = new Callable<String>() { 5             public String call() throws Exception { 6                 Thread.sleep(1000 * 5); 7                 return "线程执行完成."; 8             } 9         };10 11         try {12             Future<String> future = exec.submit(call);13             String obj = future.get(4 * 1000, TimeUnit.MILLISECONDS); // 任务处理超时时间设置14             System.out.println("任务成功返回:" + obj);15         } catch (TimeoutException ex) {16             System.out.println("处理超时啦....");17             ex.printStackTrace();18         } catch (Exception e) {19             System.out.println("处理失败.");20             e.printStackTrace();21         }22         // 关闭线程池23         exec.shutdown();24         25         System.out.println("完毕");26     }
复制代码

当然了还有很多其他方式:
http://tech.sina.com.cn/s/2008-07-04/1051720260.shtml
http://itindex.net/blog/2010/08/11/1281486125717.html
http://darkmasky.iteye.com/blog/1115047
http://www.cnblogs.com/wasp520/archive/2012/07/06/2580101.html
http://coolxing.iteye.com/blog/1476289
http://www.cnblogs.com/chenying99/archive/2012/10/24/2737924.html

    虽然找到了终极的“必杀技”,但是此时我还是不甘心,还是想从FTPClient api本身去解决问题,但此时看来也别无它他法。只能试试:即设置被动模式又设置超时时间。经过实际测试,发现问题得以解决。下面把我的FTP工具类贴给大家分享,希望能帮到遇到同样问题的人。

复制代码
  1 import org.apache.commons.net.ftp.FTP;  2 import org.apache.commons.net.ftp.FTPClient;  3 import org.apache.commons.net.ftp.FTPFile;  4 import org.apache.commons.net.ftp.FTPReply;  5   6 import java.io.BufferedInputStream;  7 import java.io.BufferedOutputStream;  8 import java.io.File;  9 import java.io.FileInputStream; 10 import java.io.FileNotFoundException; 11 import java.io.FileOutputStream; 12 import java.io.IOException; 13 import java.io.InputStream; 14 import java.io.OutputStream; 15 import java.net.UnknownHostException; 16 import java.util.ArrayList; 17 import java.util.List; 18  19 public class FtpUtil { 20     public static final String ANONYMOUS_LOGIN = "anonymous"; 21     private FTPClient ftp; 22     private boolean is_connected; 23  24     public FtpUtil() { 25         ftp = new FTPClient(); 26         is_connected = false; 27     } 28      29     public FtpUtil(int defaultTimeoutSecond, int connectTimeoutSecond, int dataTimeoutSecond){ 30         ftp = new FTPClient(); 31         is_connected = false; 32          33         ftp.setDefaultTimeout(defaultTimeoutSecond * 1000); 34         ftp.setConnectTimeout(connectTimeoutSecond * 1000); 35         ftp.setDataTimeout(dataTimeoutSecond * 1000); 36     } 37  38     /** 39      * Connects to FTP server. 40      *  41      * @param host 42      *            FTP server address or name 43      * @param port 44      *            FTP server port 45      * @param user 46      *            user name 47      * @param password 48      *            user password 49      * @param isTextMode 50      *            text / binary mode switch 51      * @throws IOException 52      *             on I/O errors 53      */ 54     public void connect(String host, int port, String user, String password, boolean isTextMode) throws IOException { 55         // Connect to server. 56         try { 57             ftp.connect(host, port); 58         } catch (UnknownHostException ex) { 59             throw new IOException("Can't find FTP server '" + host + "'"); 60         } 61  62         // Check rsponse after connection attempt. 63         int reply = ftp.getReplyCode(); 64         if (!FTPReply.isPositiveCompletion(reply)) { 65             disconnect(); 66             throw new IOException("Can't connect to server '" + host + "'"); 67         } 68  69         if (user == "") { 70             user = ANONYMOUS_LOGIN; 71         } 72  73         // Login. 74         if (!ftp.login(user, password)) { 75             is_connected = false; 76             disconnect(); 77             throw new IOException("Can't login to server '" + host + "'"); 78         } else { 79             is_connected = true; 80         } 81  82         // Set data transfer mode. 83         if (isTextMode) { 84             ftp.setFileType(FTP.ASCII_FILE_TYPE); 85         } else { 86             ftp.setFileType(FTP.BINARY_FILE_TYPE); 87         } 88     } 89  90     /** 91      * Uploads the file to the FTP server. 92      *  93      * @param ftpFileName 94      *            server file name (with absolute path) 95      * @param localFile 96      *            local file to upload 97      * @throws IOException 98      *             on I/O errors 99      */100     public void upload(String ftpFileName, File localFile) throws IOException {101         // File check.102         if (!localFile.exists()) {103             throw new IOException("Can't upload '" + localFile.getAbsolutePath() + "'. This file doesn't exist.");104         }105 106         // Upload.107         InputStream in = null;108         try {109 110             // Use passive mode to pass firewalls.111             ftp.enterLocalPassiveMode();112 113             in = new BufferedInputStream(new FileInputStream(localFile));114             if (!ftp.storeFile(ftpFileName, in)) {115                 throw new IOException("Can't upload file '" + ftpFileName + "' to FTP server. Check FTP permissions and path.");116             }117 118         } finally {119             try {120                 in.close();121             } catch (IOException ex) {122             }123         }124     }125 126     /**127      * Downloads the file from the FTP server.128      * 129      * @param ftpFileName130      *            server file name (with absolute path)131      * @param localFile132      *            local file to download into133      * @throws IOException134      *             on I/O errors135      */136     public void download(String ftpFileName, File localFile) throws IOException {137         // Download.138         OutputStream out = null;139         try {140             // Use passive mode to pass firewalls.141             ftp.enterLocalPassiveMode();142 143             // Get file info.144             FTPFile[] fileInfoArray = ftp.listFiles(ftpFileName);145             if (fileInfoArray == null) {146                 throw new FileNotFoundException("File " + ftpFileName + " was not found on FTP server.");147             }148 149             // Check file size.150             FTPFile fileInfo = fileInfoArray[0];151             long size = fileInfo.getSize();152             if (size > Integer.MAX_VALUE) {153                 throw new IOException("File " + ftpFileName + " is too large.");154             }155 156             // Download file.157             out = new BufferedOutputStream(new FileOutputStream(localFile));158             if (!ftp.retrieveFile(ftpFileName, out)) {159                 throw new IOException("Error loading file " + ftpFileName + " from FTP server. Check FTP permissions and path.");160             }161 162             out.flush();163         } finally {164             if (out != null) {165                 try {166                     out.close();167                 } catch (IOException ex) {168                 }169             }170         }171     }172 173     /**174      * Removes the file from the FTP server.175      * 176      * @param ftpFileName177      *            server file name (with absolute path)178      * @throws IOException179      *             on I/O errors180      */181     public void remove(String ftpFileName) throws IOException {182         if (!ftp.deleteFile(ftpFileName)) {183             throw new IOException("Can't remove file '" + ftpFileName + "' from FTP server.");184         }185     }186 187     /**188      * Lists the files in the given FTP directory.189      * 190      * @param filePath191      *            absolute path on the server192      * @return files relative names list193      * @throws IOException194      *             on I/O errors195      */196     public List<String> list(String filePath) throws IOException {197         List<String> fileList = new ArrayList<String>();198         199         // Use passive mode to pass firewalls.200         ftp.enterLocalPassiveMode();201         202         FTPFile[] ftpFiles = ftp.listFiles(filePath);203         int size = (ftpFiles == null) ? 0 : ftpFiles.length;204         for (int i = 0; i < size; i++) {205             FTPFile ftpFile = ftpFiles[i];206             if (ftpFile.isFile()) {207                 fileList.add(ftpFile.getName());208             }209         }210         211         return fileList;212     }213 214     /**215      * Sends an FTP Server site specific command216      * 217      * @param args218      *            site command arguments219      * @throws IOException220      *             on I/O errors221      */222     public void sendSiteCommand(String args) throws IOException {223         if (ftp.isConnected()) {224             try {225                 ftp.sendSiteCommand(args);226             } catch (IOException ex) {227             }228         }229     }230 231     /**232      * Disconnects from the FTP server233      * 234      * @throws IOException235      *             on I/O errors236      */237     public void disconnect() throws IOException {238 239         if (ftp.isConnected()) {240             try {241                 ftp.logout();242                 ftp.disconnect();243                 is_connected = false;244             } catch (IOException ex) {245             }246         }247     }248 249     /**250      * Makes the full name of the file on the FTP server by joining its path and251      * the local file name.252      * 253      * @param ftpPath254      *            file path on the server255      * @param localFile256      *            local file257      * @return full name of the file on the FTP server258      */259     public String makeFTPFileName(String ftpPath, File localFile) {260         if (ftpPath == "") {261             return localFile.getName();262         } else {263             String path = ftpPath.trim();264             if (path.charAt(path.length() - 1) != '/') {265                 path = path + "/";266             }267 268             return path + localFile.getName();269         }270     }271 272     /**273      * Test coonection to ftp server274      * 275      * @return true, if connected276      */277     public boolean isConnected() {278         return is_connected;279     }280 281     /**282      * Get current directory on ftp server283      * 284      * @return current directory285      */286     public String getWorkingDirectory() {287         if (!is_connected) {288             return "";289         }290 291         try {292             return ftp.printWorkingDirectory();293         } catch (IOException e) {294         }295 296         return "";297     }298 299     /**300      * Set working directory on ftp server301      * 302      * @param dir303      *            new working directory304      * @return true, if working directory changed305      */306     public boolean setWorkingDirectory(String dir) {307         if (!is_connected) {308             return false;309         }310 311         try {312             return ftp.changeWorkingDirectory(dir);313         } catch (IOException e) {314         }315 316         return false;317     }318 319     /**320      * Change working directory on ftp server to parent directory321      * 322      * @return true, if working directory changed323      */324     public boolean setParentDirectory() {325         if (!is_connected) {326             return false;327         }328 329         try {330             return ftp.changeToParentDirectory();331         } catch (IOException e) {332         }333 334         return false;335     }336 337     /**338      * Get parent directory name on ftp server339      * 340      * @return parent directory341      */342     public String getParentDirectory() {343         if (!is_connected) {344             return "";345         }346 347         String w = getWorkingDirectory();348         setParentDirectory();349         String p = getWorkingDirectory();350         setWorkingDirectory(w);351 352         return p;353     }354 355     /**356      * Get directory contents on ftp server357      * 358      * @param filePath359      *            directory360      * @return list of FTPFileInfo structures361      * @throws IOException362      */363     public List<FfpFileInfo> listFiles(String filePath) throws IOException {364         List<FfpFileInfo> fileList = new ArrayList<FfpFileInfo>();365         366         // Use passive mode to pass firewalls.367         ftp.enterLocalPassiveMode();368         FTPFile[] ftpFiles = ftp.listFiles(filePath);369         int size = (ftpFiles == null) ? 0 : ftpFiles.length;370         for (int i = 0; i < size; i++) {371             FTPFile ftpFile = ftpFiles[i];372             FfpFileInfo fi = new FfpFileInfo();373             fi.setName(ftpFile.getName());374             fi.setSize(ftpFile.getSize());375             fi.setTimestamp(ftpFile.getTimestamp());376             fi.setType(ftpFile.isDirectory());377             fileList.add(fi);378         }379 380         return fileList;381     }382 383     /**384      * Get file from ftp server into given output stream385      * 386      * @param ftpFileName387      *            file name on ftp server388      * @param out389      *            OutputStream390      * @throws IOException391      */392     public void getFile(String ftpFileName, OutputStream out) throws IOException {393         try {394             // Use passive mode to pass firewalls.395             ftp.enterLocalPassiveMode();396             397             // Get file info.398             FTPFile[] fileInfoArray = ftp.listFiles(ftpFileName);399             if (fileInfoArray == null) {400                 throw new FileNotFoundException("File '" + ftpFileName + "' was not found on FTP server.");401             }402 403             // Check file size.404             FTPFile fileInfo = fileInfoArray[0];405             long size = fileInfo.getSize();406             if (size > Integer.MAX_VALUE) {407                 throw new IOException("File '" + ftpFileName + "' is too large.");408             }409 410             // Download file.411             if (!ftp.retrieveFile(ftpFileName, out)) {412                 throw new IOException("Error loading file '" + ftpFileName + "' from FTP server. Check FTP permissions and path.");413             }414 415             out.flush();416 417         } finally {418             if (out != null) {419                 try {420                     out.close();421                 } catch (IOException ex) {422                 }423             }424         }425     }426 427     /**428      * Put file on ftp server from given input stream429      * 430      * @param ftpFileName431      *            file name on ftp server432      * @param in433      *            InputStream434      * @throws IOException435      */436     public void putFile(String ftpFileName, InputStream in) throws IOException {437         try {438             // Use passive mode to pass firewalls.439             ftp.enterLocalPassiveMode();440             441             if (!ftp.storeFile(ftpFileName, in)) {442                 throw new IOException("Can't upload file '" + ftpFileName + "' to FTP server. Check FTP permissions and path.");443             }444         } finally {445             try {446                 in.close();447             } catch (IOException ex) {448             }449         }450     }451 }
0 0
原创粉丝点击