Apache FTPClient几乎下载时无法检索文件

时间:2018-01-04 11:07:54

标签: java ftp apache-commons-net

Apache的FTPClient无法下载FileZilla完全下载的文件。

基本上,我在成功登录和列出后尝试做的是下载一个特定文件:

FTPClient client = new FTPClient();
client.setDataTimeout(20000);
client.setConnectTimeout(20000);
client.setBufferSize(65536);
//...
client.connect(host);
client.login(user, pswd);
// response validation
client.enterLocalPassiveMode();
// some listings with validations

InputStream in = new BufferedInputStream(client.retrieveFileStream(ftpFilePath), 16384);
// ...
byte[] buffer = new byte[8192];
while ((rd = in.read(buffer)) > 0) {
// .. reading the file and updating download progress

可以使用FTPClient的文件下载轻松替换最后一行,但结果几乎相同,但我们无法跟踪下载进度:

client.setControlKeepAliveTimeout(30);
client.retrieveFile(ftpFilePath, new org.apache.commons.io.output.NullOutputStream());

作为所有这些操作的结果,我可以看到文件正在下载,直到非常接近100%然后发生异常:

java.net.SocketTimeoutException: Read timed out
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(Unknown Source)
        at java.net.SocketInputStream.read(Unknown Source)
        at java.net.SocketInputStream.read(Unknown Source)
        at java.io.FilterInputStream.read(Unknown Source)
        at java.io.BufferedInputStream.fill(Unknown Source)
        at java.io.BufferedInputStream.read1(Unknown Source)
        at java.io.BufferedInputStream.read(Unknown Source)
        at java.io.FilterInputStream.read(Unknown Source)
        at <my code from here on>

似乎没有防火墙,但是当互联网连接速度更好时,下载成功(可能会遇到某种超时)。我认为问题在于连接,但事情是FileZilla成功下载相同的文件。

所以,我可以像这样重新提出我的问题:如何在下载文件时使FTPClient像FileZilla一样行事。可能在ping逻辑上有一些复杂的重试,我不知道。

Commons Net:commons-net-3.6

FTP服务器:具有默认配置的CentOS 5.8上的proftpd-1.3.3g-6.el5,不支持FTP over TLS。

2 个答案:

答案 0 :(得分:0)

似乎是由以下行定义的数据超时引起的:

client.setDataTimeout(20000);

根据JavaDoc:

  

设置从数据连接读取时使用的超时(以毫秒为单位)。如果值≥0,则在打开数据连接后立即设置此超时。

     

注意:在建立活动本地数据连接时调用accept()时也会应用超时。

     

参数:timeout - 打开数据连接套接字时使用的默认超时(以毫秒为单位)。值0表示无限超时。

您是否可以尝试将此值设置为0(在此上下文中表示无限)?

答案 1 :(得分:0)

我不知道这种现象的实际原因是什么(在文件的最后一块上超时),但是我已经使用Wireshark检查了FileZilla下载文件并发现它遭受的影响来自相同超时的同一问题,它重新连接到服务器并发送REST FTP查询以重新启动此特定文件的下载,从中止时仅下载最后一个块。

因此,解决方案是在下载过程中添加某种重试逻辑,以便这段代码:

InputStream in = new BufferedInputStream(client.retrieveFileStream(ftpFilePath), 16384);
// ...
byte[] buffer = new byte[8192];
while ((rd = in.read(buffer)) > 0) {

成为这个:

InputStream in = new BufferedInputStream(client.retrieveFileStream(ftpFilePath), 16384);
// ...
byte[] buffer = new byte[8192];
long totalRead = 0;
for (int resumeTry = 0; resumeTry <= RESUME_TRIES; ++resumeTry) {
    try {
        while ((rd = in.read(buffer)) > 0) {
            //...
            totalRead += rd;
        }
        break;
    } catch (SocketTimeoutException ex) {
        // omitting exception handling
        in.close();
        client.abort();
        client.connect(...);
        client.login(...);
        client.setFileType(FTPClient.BINARY_FILE_TYPE);
        client.enterLocalPassiveMode();
        client.setRestartOffset(totalRead);
        in = client.retrieveFileStream(...);
        if (in == null) {
            // the FTP server doesn't support REST FTP query
            throw ex;
        }
        in = new BufferedInputStream(in, 16384);
    }
}