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 のファイルを 1 秒未満でアップロードしましたが、Apache Commons FTP では約 1 分かかりました。

スレッドで異なるファイルを転送したい場合は、すべてclient.upload(f)を別のスレッドに入れてみてください。


@fgeの以前の回答を引用:

基本的に、できない可能性があります。

FTP には、コマンド チャネルとデータ チャネルの 2 種類のチャネルがあることを忘れないでください。1 つのアップロードは、適切なアップロード用のデータ チャネルを開くコマンド チャネルを介して命令を送信することによって開始されます。

今:

  • ほとんどの FTP サーバーは、1 つのコマンド チャネルが一度に 1 つのデータ チャネルしか開けないように設定されています。
  • 帯域幅の制限があります。アップストリームの帯域幅とサーバーのダウンストリームの帯域幅です。

複数のファイルを並行してアップロードする、つまり複数のデータ チャネルを開くことが可能である場合、TCP 自体のオーバーヘッドによって実際にアップロード プロセスが一般的に遅くなるという問題が発生します。

基本的には、常に 1 つのデータ チャネルを開いたままにします。複数のアプリを開こうとしても、それだけの価値はありません。一般的に、ケースの ~ 1% で機能する可能性があります。これは面倒なだけの価値はありません。

于 2013-07-07T10:37:34.570 に答える
0

この Q&A は、何が起こっているのかについてのいくつかの可能な説明: java 7 で ftp アップロードが遅いのはなぜですか?

さらに、いくつかの回避策を提供します。

  • (現在)ここにある 3.3 スナップショットにアップグレードします。

  • コールしFTPClient.setBufferSize(0)ます。

Java 7 for Windows には、FTP クライアントのファイアウォール アプリケーション フィルタがクライアントによる PASV モード FTP の使用をブロックしているというリグレッションもあるようです。それに対する最善の解決策が何であるかは明確ではありませんが、次のことを試すことができます。

  • Windows ファイアウォールを変更して、ファイアウォール アプリケーション フィルターを無効にします (Microsoft KB ページで説明されています。

  • 「アクティブ」モードを使用するように FTP アプリケーションを変更します。ただし、これには、FTP サーバーがクライアントを実行しているマシンへの接続を開始できる必要があります。

注: 問題には複数の説明があるように思われます...または、複数の可能性のある問題がある可能性があります。

于 2013-07-07T11:04:44.913 に答える