You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache FTP客户端被动模式调用listFiles出现27错误的排查与解决

问题

我尝试使用Apache FTP客户端在被动模式下获取FTP文件列表,相关代码如下:

连接方法

public void connect() throws IOException {
    ftp.connect(host, port);
    if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
        ftp.disconnect();
        throw new IOException("Exception in connecting to FTP Server");
    }
    ftp.enterLocalPassiveMode();
    if (!ftp.login(login, password)) {
        ftp.disconnect();
        throw new IOException("Failed to login ftp server");
    }
}

获取文件列表方法

public List<String> getPaths(String path, String date)  {
        try {
         val listFiles = ftp.listFiles(path);//1 
         val listNames = ftp.listNames(path);//2
    return null;

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

我循环调用getPaths方法遍历1000+文件夹,使用ftp.listFiles(path)时,每千余次调用会出现1-2次异常:

org.apache.commons.net.MalformedServerReplyException: Could not parse response code.
Server Reply: 27 Entering Passive Mode (10,141,166,68,126,88).

使用ftp.listNames(path)则无此异常;移除ftp.enterLocalPassiveMode()改用默认主动模式也无异常,但我无法使用主动模式,仍需调用ftp.listFiles(path)。

相关日志:

PASV
227 Entering Passive Mode (95,167,245,94,129,168)
LIST /out/published/Jamalo-Nenetckii_AO/purchaseNoticeEP/daily
150 Here comes the directory listing.
226 Directory send OK.
PASV
227 Entering Passive Mode (95,167,245,94,129,0)
LIST /out/published/Jamalo-Nenetckii_AO/purchaseNoticeIS/daily
150 Here comes the directory listing.
226 Directory send OK.
PASV
org.apache.commons.net.MalformedServerReplyException: Could not parse response code.
Server Reply: 27 Entering Passive Mode (10,141,166,68,118,198).

Wireshark抓包显示服务器返回了错误的IP地址。请问该异常的原因是什么,如何解决?

原因分析
  • 服务器响应格式异常:正常被动模式响应以227开头,但异常时服务器返回27 Entering Passive Mode...,缺失了开头的2,导致Apache FTP客户端无法解析标准FTP响应码,直接抛出MalformedServerReplyException。
  • 高并发下服务器不稳定:遍历1000+文件夹属于高频请求,服务器负载过高或网络波动时,FTP服务生成被动模式响应出现BUG,既返回了错误IP,又出现了响应码截断的情况。
  • API处理逻辑差异:listFiles需要建立数据连接获取详细文件元信息,对被动模式的依赖更强;而listNames处理逻辑更简洁,可能复用连接或跳过了部分易触发异常的流程,主动模式则是客户端发起数据连接,服务器响应逻辑不同,因此都未触发该问题。
解决方案
  1. 增加异常重试逻辑
    针对偶发的MalformedServerReplyException,在调用listFiles时添加重试机制,重试2-3次基本能覆盖大部分异常场景:
public List<String> getPaths(String path, String date) {
    int retryTimes = 3;
    while (retryTimes > 0) {
        try {
            FTPFile[] files = ftp.listFiles(path);
            List<String> result = new ArrayList<>();
            if (files != null) {
                for (FTPFile file : files) {
                    result.add(file.getName());
                }
            }
            return result;
        } catch (MalformedServerReplyException e) {
            retryTimes--;
            if (retryTimes == 0) {
                e.printStackTrace();
                return null;
            }
            // 重试前短暂休眠,降低服务器压力
            try {
                Thread.sleep(100);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
    return null;
}
  1. 手动指定被动模式IP
    如果服务器返回的被动模式IP错误,可通过setPassiveHost强制指定正确的FTP服务器IP,绕过服务器返回的错误地址:
public void connect() throws IOException {
    ftp.connect(host, port);
    if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
        ftp.disconnect();
        throw new IOException("Exception in connecting to FTP Server");
    }
    ftp.enterLocalPassiveMode();
    // 手动设置被动模式使用的服务器IP,替换为实际FTP主机地址
    ftp.setPassiveHost(host);
    if (!ftp.login(login, password)) {
        ftp.disconnect();
        throw new IOException("Failed to login ftp server");
    }
}
  1. 升级Apache Commons Net版本
    检查当前使用的版本,旧版本可能存在对异常响应处理不健壮的问题,升级到最新稳定版(如3.10.0+),能优化对非标准响应的兼容逻辑。

  2. 降低请求频率
    在遍历文件夹的循环中增加短暂休眠(50-100ms),减少服务器瞬时负载,降低响应异常的概率。

内容的提问来源于stack exchange,提问作者user19282140

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 08:55:23