【所屬類包】
org.apache.commons.net.ftp.FTPClient
【現象描述】
這兩天java項目中用到了FTP下載,像之前的項目寫好代碼,但是點擊下載后,程序調試到下面這一行,就沒反應了。
InputStream ins = ftpClient.retrieveFileStream(filePath);
沒反應還不打緊,再次點擊下載,代碼都根本不會走到這個方法了,除非重啟服務,調試的無比蛋疼。
本以為是文件夾定位錯誤,就想看看到底能不能拿到目錄下的文件列表
FTPFile[] fs = ftpClient.listFiles();
結果,程序走到這一段就又沒消息了。。。
【解決辦法】
調用 FTPClient.listFiles()或者FTPClient.retrieveFile()方法前,先調用一下FTPClient.enterLocalPassiveMode()
【測試范例】
示例1:
ftpClient.enterLocalPassiveMode();
FTPFile[] fs = ftpClient.listFiles();
示例2:
ftpClient.enterLocalPassiveMode();
InputStream ins = ftpClient.retrieveFileStream(remotefilePath);
示例3:
ftpClient.enterLocalPassiveMode();
InputStream ins = ftpClient.retrieveFile(remotefilePath, outputStream);
【原因分析】Last updated on 2018/07/19
FTP有兩種模式:主動模式(active mode)和被動模式(passive mode)
默認情況下是啟動的主動模式。
FTP是TCP鏈接,所以在讀取文件時,要進行三次握手。
我這里在進行調試的時候,FTP Server的防火牆是關閉的,但是服務器Tomcat是在我本地PC跑的,防火牆是開啟的,即FTP Client開防火牆了。
當FTP使用主動模式時,在三次握手后,FTP Client會開啟一個>1024的端口,FTP Server會開啟默認端口20,並主動向FTP Client發起數據連接請求。
而此時受我電腦防火牆的限制,FTP Server獲得FTP Client的數據端口后,會主動發起數據連接請求,此時會被防火牆被屏蔽的。
FTP Client無法正常接收FTP Server發送來的數據流,所以就會出現假死的現象。
如果強制FTP使用被動模式,三次握手完成后,FTP Client會開啟一個>1024的端口,並要求FTP Server開啟一個端口(>1024),被動等待數據通道鏈接的開啟。
當FTP Client開啟數據傳輸通道時,FTP Server就開始被動傳輸數據,不存在被牆住的問題,文件就可以正常下載下來了。


主動模式(active mode)和被動模式(passive mode)的更多介紹,可參見以下博客:
【問題擴展】
如果你的程序在自己電腦上可以正常下載FTP文件,但是上線后確不可以了,下載文件假死,請核查是不是生產環境服務器的防火牆沒有關閉。
如果有特殊原因,生產環境服務器防火牆要開着,那建議在寫Java代碼讀取文件時,用上FTPClient.enterLocalPassiveMode()。