大家好,我是一直默默努力的大齡程序媛大丫姨,今天幫助同事解決一個(gè)附件上傳的問(wèn)題,他反饋說(shuō)附件上傳后,代碼卡死,不報(bào)錯(cuò),無(wú)反應(yīng),結(jié)果經(jīng)過(guò)我細(xì)細(xì)排查,發(fā)現(xiàn)不支持ftp協(xié)議。
問(wèn)題
MalformedServerReplyException: Could not parse response code. Server Reply: SSH-2.0-OpenSSH 異常。
場(chǎng)景
Java后臺(tái)使用org.apache.commons.net.ftp.FTPClient 連接服務(wù)器,進(jìn)行FTP操作 :上傳/下載文件。
異常
org.apache.commons.net.MalformedServerReplyException: Could not parse response code.
Server Reply: SSH-2.0-OpenSSH
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:337)
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:294)
at org.apache.commons.net.ftp.FTP._connectAction_(FTP.java:400)
at org.apache.commons.net.ftp.FTPClient._connectAction_(FTPClient.java:924)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:183)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:203)
at com.web.util.FtpUtil.connectFTP(FtpUtil.java:351)
at com.web.util.FtpUtil.main(FtpUtil.java:375)
null
Process finished with exit code 0
原因
一般我們使用 Xftp 或 FileZilla這些軟件去連接服務(wù)器(FTP/SFTP)都是可以連接到。區(qū)別就是連接的參數(shù)中
協(xié)議:ftp 或 sftp
端口:21/22
但是在Java后臺(tái)使用org.apache.commons.net.ftp.FTPClient通過(guò)協(xié)議SSH2進(jìn)行SFTP連接時(shí)就會(huì)報(bào)如上錯(cuò)誤,原因是它不支持這種方式的連接(使用FTPSClient的SSL也是不行的)。
解決方法
有兩種:
第一種:Java后臺(tái)繼續(xù)使用org.apache.commons.net.ftp.FTPClient工具,然后把服務(wù)器開啟FTP協(xié)議。
第二種:換一種連接方式,使用 com.jcraft.jsch.ChannelSftp 代替org.apache.commons.net.ftp.FTPClient。
使用第二種需要引入 com.jcraft.jsch.ChannelSftp 架包。SpringBoot項(xiàng)目在pom中直接引入,其他的可自行下載。
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.49</version>
</dependency>
使用第一種方法需要找運(yùn)維去把服務(wù)器開啟SFTP協(xié)議,一般你個(gè)小開發(fā)人家能不能幫你或者基于一些安全考慮能不能開啟都是問(wèn)題。所以這里推薦使用第二種方法。
ChannelSftp 和 FTPClient 的一些使用中區(qū)別:
1. 連接方式
FTPClient的連接
FTPClient ftp = new FTPClient();
ftp.connect(ip, port);
//下面三行代碼必須要,而且不能改變編碼格式
ftp.setControlEncoding("UTF-8");
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
conf.setServerLanguageCode("zh");
//如果采用默認(rèn)端口,可以使用ftp.connect(url) 的方式直接連接FTP服務(wù)器
ftp.login(userName, password);//登錄
ChannelSftp 連接
JSch jsch = new JSch();
if (privateKey != null) {
jsch.addIdentity(privateKey);// 設(shè)置私鑰
}
Session session = jsch.getSession(userName, host, port);
if (password != null) {
session.setPassword(password);
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
ChannelSftp sftp = (ChannelSftp) channel;
2、主要功能方法
切換到 directory 目錄
ChannelSftp.cd(directory);
FtpClient.changeWorkingDirectory(ftpFilePath);
上傳文件
ChannelSftp.put(FileInputStream, fileName) ;
FtpClient.storeFile(fileName, FileInputStream);
下載文件
ChannelSftp.get(fileName, new FileOutputStream(file));
FtpClient.retrieveFileStream() 或 FtpClient.retrieveFile
還有需要注意的就是關(guān)閉的連接的方法,ChannelSftp需要你把 Session也關(guān)閉掉。