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端口向客户端的该端口发送连接请求,建立一条数据链路来传送数据。在数据链路建立过程中是服务器主动请求,所以称为主动模式。

graph LRsubgraph 客户端client[客户端]clientPort[打开端口]client--2.打开-->clientPortendsubgraph 服务端server[服务端]server-->port21server-->port20port21(21端口)port20(20端口)client--1.连接请求-->port21client--3.PORT通知-->serverport20--4.建立连接-->clientPortend

如果是被动模式的话,FTP客户端向服务器的FTP控制端口(默认21)发送连接请求,服务器接受连接,建立一条命令链路;当需要传送数据时,服务器在命令链路上用PASV命令告诉客户端,我打开了某端口,你过来连我。于是客户端向服务器的该端口发送连接请求,建立一条数据链路来传送数据。在数据链路建立的过程中是服务器被动等待客户机的请求,所以称被动模式。

graph LRsubgraph 客户端client[客户端]clientConnect[建立数据链路]client-->clientConnectendsubgraph 服务端server[服务端]server-->port21server--2.打开-->serverPortport21(21端口)serverPort(打开端口)client--1.连接请求-->port21server--3.PASV通知-->clientclientConnect--4.建立连接-->serverPortend

上文说到代码中用的是被动模式,也就意味着服务端需要打开21端口和另一个端口供客户端连接。我使用的ftp是用docker装的,只透出了21端口,所以使用被动模式连接会出错。于是把这里改成主动模式,连接成功。

12-18 03:07
查看更多