Java FTPSClient retrievefile()挂起
我正在创建一个ApacheFTPS客户端(因为远程服务器不允许普通FTP)。我可以毫无问题地连接和删除文件,但在使用retrieveFile()或retrieveFileStream()时,它会挂起 出于某些原因,非常小的文件会传输(最多5792字节),但其他任何文件都会提供以下PrintCommandListener输出: 运行:Java FTPSClient retrievefile()挂起,java,apache,ftp,ftp-client,ftps,Java,Apache,Ftp,Ftp Client,Ftps,我正在创建一个ApacheFTPS客户端(因为远程服务器不允许普通FTP)。我可以毫无问题地连接和删除文件,但在使用retrieveFile()或retrieveFileStream()时,它会挂起 出于某些原因,非常小的文件会传输(最多5792字节),但其他任何文件都会提供以下PrintCommandListener输出: 运行: 220------欢迎来到纯FTPd[privsep][TLS]------ 220您是50个用户中的第2个。 当地时间现在是19点42分。服务器端口:21。 22
220------欢迎来到纯FTPd[privsep][TLS]------
220您是50个用户中的第2个。
当地时间现在是19点42分。服务器端口:21。
220这是一个专用系统-无匿名登录
此服务器上也欢迎220-IPv6连接。
220您将在15分钟不活动后断开连接。
认证TLS
234认证TLS正常。
用户
331用户确认。需要密码
通过
230好的。当前受限目录为/
A型
200类型现在是ASCII
EPSV
229扩展被动模式正常(| | | | 53360 |)
RETR test.txt
150接受的数据连接
150 7.3千字节可供下载 代码如下:
try {
FTPSClient ftpClient = new FTPSClient("tls",false);
ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
ftpClient.connect(host, port);
int reply = ftpClient.getReplyCode();
if (FTPReply.isPositiveCompletion(reply)) {
ftpClient.enterLocalPassiveMode();
ftpClient.login(username, password);
ftpClient.enterLocalPassiveMode();
FileOutputStream outputStream = new FileOutputStream(tempfile);
ftpClient.setFileType(FTPClient.ASCII_FILE_TYPE);
ftpClient.retrieveFile("test.txt", outputStream);
outputStream.close();
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException ioe) {
System.out.println("FTP client received network error");
}
非常感谢您的任何想法。通常,FTPS连接的FTP命令顺序是(per)
AUTH TLS
,PBSZ 0
,然后是USER
,PASS
,等等。因此,您可以尝试:
FTPSClient ftpClient = new FTPSClient("tls",false);
ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
ftpClient.connect(host, port);
int reply = ftpClient.getReplyCode();
if (FTPReply.isPositiveCompletion(reply)) {
ftpClient.execPBSZ(0);
reply = ftpClient.getReplyCode();
// Check for PBSZ error responses...
ftpClient.execPROT("P");
reply = ftpClient.getReplyCode();
// Check for PROT error responses...
ftpClient.enterLocalPassiveMode();
这明确地告诉服务器不要缓冲数据连接(PBSZ 0
),并使用TLS保护数据传输(PROT p
)
您能够传输一些字节这一事实表明问题是而不是防火墙/路由器/NAT的常见问题,这是另一个常见的FTPS问题
希望这有帮助 即使以正确的顺序调用了
PBSZ 0
和PROT p
,有时服务器确实需要SSL会话重用,而客户端默认情况下并非如此
例如,当尝试列出目录时,会出现以下回复。因此,不会返回任何内容列表,这样客户端就会看到目录为空:
LIST /
150 Here comes the directory listing.
522 SSL connection failed; session reuse required: see require_ssl_reuse option in sftpd.conf man page
为了克服这一问题,需要通过重写\u prepareDataSocket()
方法对FTPSClient进行自定义初始化
此处详细说明了解决方案:
从上述链接获取的工作代码示例:
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Locale;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import org.apache.commons.net.ftp.FTPSClient;
import com.google.common.base.Throwables;
public class SSLSessionReuseFTPSClient extends FTPSClient {
// adapted from: https://trac.cyberduck.io/changeset/10760
@Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if(socket instanceof SSLSocket) {
final SSLSession session = ((SSLSocket) _socket_).getSession();
final SSLSessionContext context = session.getSessionContext();
try {
final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
sessionHostPortCache.setAccessible(true);
final Object cache = sessionHostPortCache.get(context);
final Method putMethod = cache.getClass().getDeclaredMethod("put",Object.class, Object.class);
putMethod.setAccessible(true);
final Method getHostMethod = socket.getClass().getDeclaredMethod("getHost");
getHostMethod.setAccessible(true);
Object host = getHostMethod.invoke(socket);
final String key = String.format("%s:%s", host, String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
putMethod.invoke(cache, key, session);
} catch(Exception e) {
throw Throwables.propagate(e);
}
}
}
}
希望几年后有人觉得我的评论有用。
在我的例子中,我将
retrieveFile
替换为retrieveFileStream
。它需要更多的代码,但至少可以工作。请正确设置代码格式。您只需标记它,然后使用编辑器顶部提供的工具点击格式化即可。谢谢,这很有用,因为我有点期待它会成为防火墙。但该代码不起作用,防止挂起,但输出“FTP客户端收到网络错误”。我想知道FTPSClient
是否不希望数据传输受到保护。如果改用execPROT(“C”)
会发生什么?这会告诉服务器清除数据传输保护通道(仅用于测试)。您还可以使用lftp
(或其他FTPS客户端)下载文件,再次检查服务器(以及任何防火墙/NATs/路由器)是否正常工作。如果成功了,那么你就知道这是找到正确代码的问题;如果不是,那么问题可能不在您的代码中,而是在其他地方。我可以使用filezilla(浏览器超时)下载服务器文件。我们将研究lftp,再次感谢。虽然此链接可以回答问题,但最好在此处包含答案的基本部分,并提供链接供参考。如果链接页面发生更改,仅链接的答案可能无效。@MattKe谢谢,在这里发布了工作代码示例。