我使用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毫秒,与没有线程的代码相同。
我用内联网测试速度。上网需要更多的时间。
我应该如何提高上传速度?
我不知道为什么,但Apache Commons FTP上传速度很慢,我也遇到了同样的问题,无法解决。
现在我使用FTP4j,它与apachecommons 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%的情况下有效。这不值得这么麻烦。
这个问答;关于正在发生的事情的一些可能的解释:为什么在java7中ftp上传缓慢
此外,它还提供了几个解决方案:
-
升级到3.3快照,可以(目前)在这里找到
-
呼叫
FTPClient.setBufferSize(0)
。
显然,Java 7 for Windows中也有一个回归,其中FTP客户端的防火墙应用程序过滤器阻止客户端使用PASV模式FTP。目前还不清楚最好的解决方案是什么,但你可以尝试以下方法:
-
更改Windows防火墙以禁用防火墙应用程序筛选器(如Microsoft知识库页面.中所述
-
将FTP应用程序更改为使用"活动"模式。。。尽管这需要FTP服务器可以启动与运行客户端的机器的连接。
注意:这个问题似乎有不止一种解释。。。或者可能不止一个可能的问题。