JAVA]Apache FTPClient操作「カード死」問題の分析と解決

15799 ワード

最近では、サードパーティとの連携で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は、多くの人がこのような問題を抱えていることに気づき、最終的には1つの投稿で解決策を見つけた.この2つのメソッドを呼び出す前に、FTPClientを呼び出す.enterLocalPassiveMode();この方法は、データ接続のたびにftpクライアントが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 call = new Callable() { 5       public String call() throws Exception { 6         Thread.sleep(1000 * 5); 7         return "      ."; 8       } 9     };10 11     try {12       Future 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 list(String filePath) throws IOException {197     List fileList = new ArrayList();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 listFiles(String filePath) throws IOException {364     List fileList = new ArrayList();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 }