我正在尝试处理某个FTP目录,该目录包含多个目录,而这些目录又包含任意数量的文件。所以我要做的是为每个子目录设置一个线程,并且每个线程都关注相应的子目录,这就是我的想法:
private void fetchFilesFromFTP() {
try {
client.connect("ftp.ncbi.nih.gov");
client.login("anonymous", "anonymous");
client.changeWorkingDirectory("genomes/Fungi");
FTPFile dirs[] = client.listDirectories();
dirsToDl.addAndGet(dirs.length);
for (final FTPFile ftpFile : dirs) {
exec.execute(new Runnable() {
//process each FTP directory in a new thread
@Override
public void run() {
processFTPdir(ftpFile.getName());
}
});
}
} catch (SocketException ex) {
Logger.getLogger(FungiProcessor.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(FungiProcessor.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void processFTPdir(String dir) {
File f = new File(destination + File.separator + dir);
if (!f.mkdirs()) {
System.out.println("Error creating dir for " + dir);
return;
}
FTPFile files[];
try {
//we are already in the correct directory
files = client.listFiles(dir, new FTPFileFilter() {
@Override
public boolean accept(FTPFile ftpf) {
return ftpf.getName().endsWith(".gbk");
}
});
for (FTPFile fTPFile : files) {
FileOutputStream fout = new FileOutputStream(destination + File.separator + dir + File.separator + fTPFile.getName());
if (client.retrieveFile(dir + "/" + fTPFile.getName(), fout)) {
System.out.println("successfully downloaded");
fout.flush();
fout.close();
}
System.out.println(client.getReplyString());
}
} catch (IOException ex) {
Logger.getLogger(FungiProcessor.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if(dirsToDl.decrementAndGet() == 0) latch.countDown();
}
}
代码的顺序版本工作 - 我可以看到实际上正在下载以.gbk结尾的文件,而对于多线程版本,只创建了相应的子目录,但没有下载任何文件。我甚至没有任何错误。 FTP有可能不支持一次下载多个文件吗?
答案 0 :(得分:3)
更好的方法是让自己的客户端连接到服务器,并创建要下载的文件的路径列表。然后启动一些线程,每个线程都有自己的客户端,从列表中抓取第一个并开始下载文件。
您可以拥有一台ftp-server的并发连接数受该服务器设置的限制。
答案 1 :(得分:1)
我非常怀疑这适用于多个线程。所有线程将共享相同的客户端(因此,相同的命令通道),并且FTP不提供一种方法来执行您需要的多路复用以使其工作。 (对两个线程命令的响应看起来几乎相同; FTP没有办法说“这是对命令1的响应”。)
你必须让每个线程在发送命令之前获取一个锁,并在获得响应后解锁...一次强制一个命令运行,并将事物序列化太多以至于你失去了所有的好处无论如何多线程。唯一的方法是每个线程使用一个客户端,但最终你会遇到每用户连接限制(这往往非常严格)。
总而言之,这听起来像是单个线程的工作。