JAVA]Apache FTPClient操作「カード死」問題の分析と解決
最近では、サードパーティとの連携で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の解決です.
もちろん他にもいろいろあります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ツール類を皆さんに貼って共有して、同じ問題に直面した人を助けることができることを望んでいます.
この問題を解決するために、まず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 }