Apache Commons Net 提供了 FTPHTTPClient,用于通过 HTTP CONNECT 代理使用 FTP。它的有用之处不只是能到达 FTP 登录端口:它还会为目录列表和文件传输所使用的被动数据连接建立代理隧道。

构造函数接受代理端点以及可选的代理凭据。FTP 服务器仍是 connect 的参数,而 login 接收 FTP 账户:

FTPHTTPClient ftp = new FTPHTTPClient(
    "proxy.example.net", 8080, proxyUser, proxyPassword
);

try {
    ftp.connect("ftp.example.net", 21);
    if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
        throw new IOException(ftp.getReplyString());
    }
    if (!ftp.login(ftpUser, ftpPassword)) {
        throw new IOException(ftp.getReplyString());
    }

    ftp.enterLocalPassiveMode();
    if (!ftp.setFileType(FTP.BINARY_FILE_TYPE)
            || !ftp.retrieveFile(remoteFile, output)) {
        throw new IOException(ftp.getReplyString());
    }
    ftp.logout();
} finally {
    if (ftp.isConnected()) {
        ftp.disconnect();
    }
}

这段传输代码假定已有 Commons Net 的 FTP 导入、java.io.IOException、由应用程序提供的凭据变量,以及调用方拥有的输出流。remoteFile 是预期的服务器端文件名。应用程序还必须在适当的时候关闭自己的输出流。

请在连接之后、数据操作之前设置被动模式。FTPHTTPClient 的实现会拒绝主动模式下的数据连接。在被动模式下,它会与 FTP 协商数据端点,并向 HTTP 代理再打开一条连接,然后请求一条通往该端点的 CONNECT 隧道。

第二条隧道说明了一个常见边界:仅有针对 FTP 控制端口的代理许可还不够。允许的目标和端口必须包含服务器的被动数据端点。问候和登录成功只能证明控制路径是通的。

当代理用户名和密码都存在时,该实现会提供 Basic 代理认证。这与 FTP 的 USER/PASS 交换不是一回事。若代理要求其他认证机制,就需要兼容的连接器;构造函数并不会让所有方案都可用。

Commons Net 将该类记录为实验性,因此在采用之前应结合应用与代理的要求进行评估。它是 FTP 客户端,而不是 FTPS 或 SFTP 客户端。对于有可用 SOCKS 服务的 FTPS,FTPSClient 的套接字代理接口是另一条客户端路径。CONNECT 提供的是通路,它本身并不加密 FTP 凭据或文件内容。

请让错误与产生它的阶段对应起来:隧道建立期间的 HTTP 拒绝、FTP 登录被拒,或 FTP/数据传输失败。更换 FTP 密码无法解决拒绝被动数据端口的代理策略。

相关文章