为何FTP服务器可检索文件名却无法获取文件对象?
问题描述
使用Apache Commons Net库访问专用硬件搭载的嵌入式FTP服务器时出现异常:listNames()方法可正常返回结果,但listFiles()方法无法获取有效文件对象。
硬件官方文档仅明确要求FTP连接必须使用主动模式,测试环境为无公网接入的本地WiFi网络,测试代码运行在Android应用中。
测试代码
ftpClient.connect(FTP_IP, 21); Log.e(TAG, "reply string after connect():" + ftpClient.getReplyString()); ftpClient.enterLocalActiveMode(); ftpClient.enterRemoteActiveMode(InetAddress.getByName(FTP_IP), 21); ftpClient.login(FTP_USERNAME, FTP_PASSWORD); Log.e(TAG, "reply string after login(): " + ftpClient.getReplyString()); ftpClient.sendCommand(FTPCmd.LIST); Log.e(TAG, "reply string for LIST: " + ftpClient.getReplyString()); ftpClient.sendCommand(FTPCmd.NAME_LIST); Log.e(TAG, "reply string for NLST: " + ftpClient.getReplyString()); remoteFiles = ftpClient.listFiles(); Log.e(TAG, "number of remote FTPFile objects returned: " + remoteFiles.length); remoteFileNames = ftpClient.listNames(); Log.e(TAG, "remote file names returned: " + remoteFileNames);
运行日志
2022-06-29 17:07:02.853 21263-22717/spinner.fakdown E/FTP: reply string after connect():220--- Welcome to *** FTP 220--- By *** --- 220 -- Version: 1.0 FTP-2015-04-08 - modify *** 2016-09-14 ver2 -- 2022-06-29 17:07:02.875 21263-22717/spinner.f E/FTP: reply string after login(): 230 OK. 2022-06-29 17:07:02.881 21263-22717/spinner.f E/FTP: reply string for LIST: 425 No data connection 2022-06-29 17:07:02.888 21263-22717/spinner.f E/FTP: reply string for NLST: 425 No data connection 2022-06-29 17:07:03.235 21263-22717/spinner.f E/FTP: number of remote FTPFile objects returned: 0 2022-06-29 17:07:03.485 21263-22717/spinner.f E/FTP: remote file names returned: 6
从返回的425错误码初步判断问题和FTP主被动模式配置相关,但多次调整配置未解决。
已完成验证项
- Android端Turbo Client应用可正常访问该FTP服务器、浏览全部文件
- 已尝试所有可用的
FTPClientConfig配置参数(如FTPClientConfig.SYST_UNIX),均未解决问题
解决方案
核心问题出在主动模式配置错误,以及Android环境下的主动模式适配问题,按以下步骤调整即可:
- 删除冗余错误的主动模式配置
代码中同时调用enterLocalActiveMode()和enterRemoteActiveMode(InetAddress.getByName(FTP_IP), 21)是完全错误的。FTP主动模式下仅需调用enterLocalActiveMode()即可,enterRemoteActiveMode属于非标准API,作用是要求服务器端进入主动连接模式,传入21端口(FTP控制通道默认端口)作为数据连接端口,会直接导致数据链路建立失败,触发425错误。 - 修复Android环境下主动模式的IP上报问题
Apache Commons Net在主动模式下会默认自动探测本机IP并通过PORT命令上报给服务器,但Android设备存在多网卡(蜂窝网卡、虚拟网卡、WiFi网卡),自动探测经常会拿到非当前WiFi局域网的IP地址,导致服务器无法回连客户端开放的数据端口,报425错误。需要在开启主动模式后手动指定上报的IP地址:ftpClient.enterLocalActiveMode(); // 自行实现getLocalWifiIpAddress()方法,获取当前设备连接WiFi后分配到的局域网IPv4地址 ftpClient.setActiveExternalIPAddress(getLocalWifiIpAddress()); // 可选:固定主动模式数据端口范围,避免系统随机分配的高端口被Android系统防火墙拦截 ftpClient.setActivePortRange(50000, 51000); - 移除手动发送原始FTP命令的测试代码
在调用封装方法前手动发送FTPCmd.LIST和FTPCmd.NAME_LIST原始命令,会提前触发服务器的数据连接尝试,打乱控制通道的状态机,导致后续封装方法调用时链路状态异常。这部分测试代码需要全部删除,不要在业务逻辑里混用原始命令和封装好的API。 - 开启宽松解析适配嵌入式FTP的非标准列表格式
这类老旧定制化嵌入式FTP服务器的LIST命令返回格式大多不完全符合标准SYST规范,不需要反复切换FTPClientConfig的系统类型参数,直接开启宽松解析即可:FTPClientConfig config = new FTPClientConfig(); config.setLenientFutureDates(true); ftpClient.configure(config); - 确认Android权限配置
主动模式下客户端需要开放本地端口监听服务器的回连请求,确保应用已经在AndroidManifest中声明android.permission.INTERNET权限,且没有被系统安全规则、防火墙限制端口监听权限。
日志里手动发送NLST命令返回425,但后续
listNames()能正常返回6个文件名,本质是第一次手动发命令时数据链路建立失败,listNames()方法内部重置了控制通道状态重新发起请求才执行成功;而listFiles()依赖LIST命令返回的完整文件属性数据,对链路稳定性、格式兼容性要求更高,配置存在问题时会直接返回空数组。
内容的提问来源于stack exchange,提问作者Spinner
相关产品推荐
相关产品推荐

