Ftp問題
最近遇到了ftp讀取中文亂碼的問題,代碼中使用的是FtpClient。google一下找到了解決方案。
FTP協議里面,規定文件名編碼為iso-8859-1,FTP類中默認的編碼也是這個。
public FTP() {
this.setDefaultPort(21);
this._replyLines = new ArrayList();
this._newReplyString = false;
this._replyString = null;
this._controlEncoding = "ISO-8859-1";
this._commandSupport_ = new ProtocolCommandSupport(this);
}
參考文章 https://www.cnblogs.com/chenfei0801/p/3427310.html 設置了編碼格式
private FTPClient getClient(ConnectReq connectReq) {
String host = StringUtils.substringBefore(connectReq.getConnectUrl(), ":");
int port = Integer.parseInt(StringUtils.substringAfter(connectReq.getConnectUrl(), ":"));
log.info("Connect to Ftp [{}:{}] with user [{}] and passwd [{}].",
host, port, ftpReq.getUserName(), ftpReq.decodePassword());
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(host, port);
log.info("Connected.");
ftpClient.login(ftpReq.getUserName(), ftpReq.decodePassword());
// Use passive mode to pass firewalls.
if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(
"OPTS UTF8", "ON"))) {
LOCAL_CHARSET = UTF8_CHARSET;
}
ftpClient.setControlEncoding(LOCAL_CHARSET);
ftpClient.enterLocalPassiveMode();
log.info("Logged.");
return ftpClient;
} catch (IOException e) {
log.error("連接ftp服務-{} 出錯,原因:{}", ftpReq.getHost(), e);
throw new InnerException(ErrorCode.DSOURCE_ERROR, e);
}
}
發現程序能夠連接成功,但是在調用client.logout方法時出現了錯誤。
org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:313)
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:290)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:479)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:552)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:601)
at org.apache.commons.net.ftp.FTP.quit(FTP.java:809)
at org.apache.commons.net.ftp.FTPClient.logout(FTPClient.java:979)
...
java.lang.RuntimeException: org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
...
Caused by: org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:313)
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:290)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:479)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:552)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:601)
at org.apache.commons.net.ftp.FTP.quit(FTP.java:809)
at org.apache.commons.net.ftp.FTPClient.logout(FTPClient.java:979)
... 28 more
根據FTPClient官方文檔的解釋
/***
* Logout of the FTP server by sending the QUIT command.
* <p>
* @return True if successfully completed, false if not.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean logout() throws IOException
{
return FTPReply.isPositiveCompletion(quit());
}
這個錯誤的出現的可能原因是ftp服務器過早關閉或者其他原因導致服務端返回了421。
繼續追蹤到后面的異常棧
Connection closed without indication.
這個問題可能是因為FTP服務器服務有故障,或是是網絡問題。於是我立馬用命令行嘗試連接到Ftp上,果然是可以連的,證明ftp服務沒問題。那么就是網絡問題了。
檢查了代碼,在代碼中使用的是被動模式。FTP服務器一般使用20和21兩個端口與客戶端進行通信,21端口用來傳輸FTP的控制命令,20端口用於傳輸文件數據。
如果是主動模式的話,FTP客戶端向服務器的FTP控制端口(默認是21)發送連接請求,服務器接受連接,建立一條命令鏈路;當需要傳送數據時,客戶端在命令鏈路上用PORT的命令告訴服務器我開放了某端口,你過來連接我。於是服務器從20端口向客戶端的該端口發送連接請求,建立一條數據鏈路來傳送數據。在數據鏈路建立過程中是服務器主動請求,所以稱為主動模式。
如果是被動模式的話,FTP客戶端向服務器的FTP控制端口(默認21)發送連接請求,服務器接受連接,建立一條命令鏈路;當需要傳送數據時,服務器在命令鏈路上用PASV命令告訴客戶端,我打開了某端口,你過來連我。於是客戶端向服務器的該端口發送連接請求,建立一條數據鏈路來傳送數據。在數據鏈路建立的過程中是服務器被動等待客戶機的請求,所以稱被動模式。
上文說到代碼中用的是被動模式,也就意味着服務端需要打開21端口和另一個端口供客戶端連接。我使用的ftp是用docker裝的,只透出了21端口,所以使用被動模式連接會出錯。於是把這里改成主動模式,連接成功。