4

我使用 org.apache.commons.net.ftp.FTPClient 实现了 java 代码将文件上传到服务器 对于多个文件,ftp 上传速度非常慢。我怎样才能提高速度。

- 改变图书馆?上传多个文件的强大的FTP客户端类库是什么?

- 使用多个线程?如何实现多线程的ftp上传功能?有人可以给我举个例子吗?我是多线程编程的新手。


阅读所有答案后,我尝试更改代码并进行测试。

以下是一个示例 FTPClient 代码:

// create instance of FTPClient
FTPClient ftp = new FTPClient();

ftp.setControlEncoding("UTF-8");
ftp.setDefaultTimeout(30000);


// connect to server
try
{
    ftp.connect("10.1.1.1", 990);
}
catch(Exception e)
{
    System.out.println("Cannot connect to server");
    return;
}

// login to server
if (!ftp.login("username", "password"))
    {
        ftp.logout();
        System.out.println("Cannot login to server");
        return;
    }

try
{
    ftp.setFileTransferMode(FTP.BINARY_FILE_TYPE);
    ftp.enterLocalPassiveMode();
    // ftp.setBufferSize(0); <-- someone suggest me to set buffer size to 0, but it throw error sometime.
}
catch(Exception e)
{
}
// create directory on server
// dirs is list of required directories on server
for (String dir : dirs)
    {
        try
        {
            ftp.makeDirectory(dir);
        }
        catch(IOException e)
        {
        }
    }


// files is a map of local file and string of remote file
// such as 
// file on client is "C://test/a.txt"
// location on server is "/test/a.txt"
for (Map.Entry<File, String> entry : files.entrySet())
    {
        File localFile = entry.getKey();
        String remoteFile = entry.getValue();

        FileInputStream input = null;
        try
        {
            input= new FileInputStream(localFile);
            ftp.storeFile(remoteFile, input);
        }
        catch (Exception e)
        {
            try
            {
                ftp.deleteFile(remoteFile);
            }
            catch (IOException e1)
            {
            }
        }
        finally
        {
            if (input != null)
            {
                try
                {
                    input.close();
                }
                catch (IOException e)
                {
                }
            }
        }
    }

// disconnect
if (ftp != null && ftp.isConnected())
    {
        try
        {
            ftp.disconnect();
        }
        catch (IOException f)
        {
            // do nothing
        }
    }

当我上传 1050 个文件(每个文件大约 1-20 KB)时,大约需要 49406 - 51000 毫秒(这只是上传时间)。我想提高速度。

有人建议我使用 ftp4j,但是当我用 1050 个文件测试库时,ftp4j 的上传速度比 FTPClient 慢大约 10000 毫秒。它花了大约 60000 毫秒。

以下是示例 ftp4j 代码:

// create instance of FTPClient 
FTPClient ftp = new FTPClient();

ftp.setCharset("UTF-8");

// connect to server
try
{
    ftp.connect("10.1.1.1", 990);
}
catch(Exception e)
{
    System.out.println("Cannot connect to server")
    return;
}

// login to server  
try
{
    ftp.login("username", "password");
}
catch (Exception e)
{
    try
    {
      ftp.logout();
    }
    catch (Exception e1)
    {
    }
    System.out.println("Cannot login to server")
    return;
}

try
{
    ftp.setType(FTPClient.TYPE_BINARY);
    ftp.setPassive(true);
}
catch(Exception e)
{   
}

// create directory on server  
// dirs is list of required directories on server  
for (String dir : dirs)
{
    try
    {
      ftp.createDirectory(dir);
    }
    catch (Exception e)
    {
    }
}

// files is a map of local file and string of remote file  
// such as   
// file on client is "C://test/a.txt"  
// location on server is "/test/a.txt" 
for (Map.Entry<File, String> entry : files.entrySet())
{
    final File localFile  = entry.getKey();
    final String remoteFile  = entry.getValue();

    BufferedInputStream input = null;
    boolean success = false;
    try
    {
      input = new BufferedInputStream(new FileInputStream(localFile));

       // ftp.upload(localFile); <-- if I use ftp.upload(File), it will took more time. 
       ftp.upload(remoteFile, input, 0, 2048, new MyTransferListener());
       success = true;
    }
    catch (Exception e)
    {
    }
    finally
    {
        if (input != null)
        {
            try
            {
                input.close();
            }
            catch (IOException e)
            {
            }
        }
        if (!success)
        {
            try
            {
              ftp.deleteFile(remoteFile);
            }
            catch (Exception e)
            {
            }
        }
    }
}

// disconnect
if (ftp != null && ftp.isConnected())  
{  
    try  
    {  
      ftp.disconnect();  
    }  
    catch (IOException f)  
    {  
      // do nothing  
    }  
} 

我尝试使用多个线程。

以下是多线程代码:

final CountDownLatch latch = new CountDownLatch(files.size());
ExecutorService pool = Executors.newFixedThreadPool(10);
for (Map.Entry<File, String> entry : files.entrySet())
{
    final File localFile = entry.getKey();                  
    final String remoteFile = entry.getValue();

    pool.execute(new Runnable() {
        public void run()
        {
            FileInputStream input = null;
            try
            {
                input= new FileInputStream(localFile);
                ftp.storeFile(remoteFile, input);
            }
            catch (Exception e)
            {
                try
                {
                    ftp.deleteFile(remoteFile);
                }
                catch (IOException e1)
                {
                }
            }
            finally
            {
                if (input != null)
                {
                    try
                    {
                        input.close();
                    }
                    catch (IOException e)
                    {
                    }
                }
               latch.countDown();
            }
        }
    });
}
try
{
  // waiting for all threads finish
  // see: http://stackoverflow.com/questions/1250643/how-to-wait-for-all-threads-to-finish-using-executorservice
  latch.await();
}
catch(Exception e)
{
}

这是对的吗?它可以正常工作,但无法提高速度。它花费了大约 49000 - 51000 毫秒,与没有线程的代码相同。

我用内网测试速度。上网需要更多时间。

我应该怎么做才能提高上传速度?

4

2 回答 2

1

我不知道为什么,但是 Apache Commons FTP 上传速度很慢,我遇到了同样的问题,我无法解决。

现在我使用FTP4j,它与 apache commons ftp 非常相似,但上传速度非常快。

这是一个例子:

FTPClient client = new FTPClient();
client.connect("www.yoursite.com");
client.login("login", "password");
client.setPassive(true);
client.setType(FTPClient.TYPE_BINARY);
client.changeDirectory("a");
File f = new File("path/to/your/file");
client.upload(f);
client.disconnect(true);

使用这个库,我在不到一秒的时间内上传了一个 340KB 的文件,而使用 Apache Commons FTP 大约需要 1 分钟。

如果您想使用线程传输不同的文件,请尝试将每个文件client.upload(f)放入不同的线程,但我不确定它是否会促进传输。


引用@fge 先前的答案:

基本上,机会是,你不能。

不要忘记 FTP 有两种类型的通道:命令通道和数据通道。通过在命令通道上发送指令以打开数据通道以进行适当的上载,从而启动一次上载。

现在:

  • 大多数FTP服务器都配置成一个命令通道在任何时候只能打开一个数据通道;
  • 有带宽限制:您的上游带宽和服务器的下游带宽。

如果可以并行上传多个文件,即打开多个数据通道,您就会遇到 TCP 本身的开销实际上会减慢上传过程的问题。

基本上:随时保持一个数据通道打开。尝试打开不止一个是不值得的。一般而言,它可能适用于约 1% 的情况。这不值得麻烦。

于 2013-07-07T10:37:34.570 回答
0

这个问答对正在发生的事情的一些可能的解释: Why is ftp upload slow in java 7

此外,它还提供了一些解决方法:

显然,Java 7 for Windows 中也有一个回归,其中 FTP 客户端的防火墙应用程序过滤器阻止客户端使用 PASV 模式 FTP。目前尚不清楚最佳解决方案是什么,但您可以尝试以下方法:

  • 更改您的 Windows 防火墙以禁用防火墙应用程序过滤器(如 Microsoft 知识库页面中所述。

  • 将您的 FTP 应用程序更改为使用“活动”模式……尽管这要求 FTP 服务器可以启动与运行您的客户端的机器的连接。

注意:对于这个问题似乎有不止一种解释......或者可能不止一种可能的问题。

于 2013-07-07T11:04:44.913 回答