多个线程从sftp服务器下载相同的文件

时间:2012-10-12 20:44:23

标签: java multithreading sftp

我有一个系统,当找到某种类型的文件时,我会在一个单独的帖子中下载,编码和上传它们。

while(true) {
    for(SftpClient c : clients) {
        try {
            filenames = c.list("*.wav", "_rdy_");
        } catch (SftpException e) {
            e.printStackTrace();
        }
        if(filenames.size() > 0) {
            //AudioThread run() method handles the download, encode, and upload
            AudioThread at = new AudioThread(filenames);
            at.setNode(c.getNode());
            Thread t = new Thread(at);
            t.start();
        }
    }
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

来自AudioThread的运行方法

public void run() {
    System.out.println("Running...");
    this.buildAsteriskMapping();
    this.connectToSFTP();
    ac = new AudioConvert();
    this.connectToS3();

    String downloadDir = "_rough/" + getNode() + "/" + Time.getYYYYMMDDDate() + "/";
    String encodeDir = "_completed" + getNode() + "/" + Time.getYYYYMMDDDate() + "/";
    String uploadDir = getNode() + "/" + Time.getYYYYMMDDDate() + "/";

    System.out.println("Downloading...");
    try {
        sftp.get(filenames, downloadDir);
    } catch (SftpException e) {
        //download failed
        System.out.println("DL Failed...");
        e.printStackTrace();
    }

    System.out.println("Encoding...");
    try {
        ac.encodeWavToMP3(filenames, downloadDir, encodeDir);
    } catch (IllegalArgumentException | EncoderException e) {
        System.out.println("En Failed...");
        e.printStackTrace();
    }

    System.out.println("Uploading...");
    try {
        s3.upload(filenames, encodeDir, uploadDir);
    } catch (AmazonClientException e) {
        System.out.println("Up Failed...");
        e.printStackTrace();
    }

}

下载方法:

public void get(ArrayList<String> src, String dest) throws SftpException {
    for(String file : src) {
        System.out.println(dest + file);
        channel.get(file, dest + file);
    }
}

编码方法:

public void encodeWavToMP3(ArrayList<String> filenames, String downloadDir, String encodeDir) throws IllegalArgumentException, EncoderException {
    for(String f : filenames) {
        File wav = new File(downloadDir + f);
        File mp3 = new File(encodeDir + wav.getName().replace(".wav", ".mp3"));
        encoder.encode(wav, mp3, attrs);
    }
}

上传方法:

public void upload(ArrayList<String> filenames, String encodeDir, String uploadDir)  throws AmazonClientException, AmazonServiceException {
    for(String f : filenames) {
        s3.putObject(new PutObjectRequest(bucketName, uploadDir, new File(encodeDir + f)));
    }
}

问题是我一直在为每个线程下载相同的文件(或大约相同的文件)。我想为每个保存正在下载的文件的客户端添加一个变量,但我不知道如何从该变量中删除列表/文件名。什么是解决方案?我的老板也希望只允许运行x个线程。

2 个答案:

答案 0 :(得分:4)

很难看到问题,因为实际下载的代码丢失了:P

但是,我会使用某种ExecutorService代替。

基本上,我会将每个下载请求添加到服务中(包含在“DownloadTask”中,其中包含对要下载的文件的引用以及获取文件可能需要的任何其他相关信息)并让服务处理其余的。

可以对下载任务进行编码,以便在您认为合适的情况下考虑现有文件。

根据您的要求,这可以是单线程或多线程服务。它还可以允许您将上传任务放入其中。

查看Executors小道以获取更多信息

一般的想法是使用一种生产者/消费者模式。您将(至少)有一个线程可以查找要下载的所有文件,对于每个文件,您可以将其添加到执行程序服务中。下载文件后,我会将请求排队并上传到同一服务中。

这样,您可以避免所有混乱的同步和线程管理:D

您可以对扫描任务使用相同的想法,对于每个客户端,您可以将任务转移到单独的服务

答案 1 :(得分:1)

您的代码中存在一个问题,您在while循环中实例化AudioThread。

请注意,在创建线程并执行t.start()之后,所有下载,编码和上载都是异步发生的。因此,在启动线程后,循环连续执行另一个对c.list(...)的调用,而您创建的第一个线程仍在处理第一组文件。很可能在随后的c.list()调用中返回相同的文件集,因为您在调用中指定了文件模式,并且没有代码标记当前正在处理哪些文件。

我的建议:

  • 使用前一篇文章中提到的Executors.newFixedThreadPool(int nThreads)。并指定计算机中处理器数量的线程数。在你的while循环之前执行此操作。
  • 对于从ftp s.list()检索的每个文件名,创建一个Callable类并调用ExecutorService.invokeAll(Collection&lt; Callable&lt; T&gt;&gt; tasks)。您将创建的Callable中的代码是您的AudioThread代码。将AudioThread代码修改为仅处理一个文件(如果可能),这样您就可以为每个文件并行执行下载,上传和编码。
  • 添加标记已处理文件的代码。我建议添加一个代码,将您处理的文件重命名为其他名称,以避免在下一次c.list()调用中返回。
  • 在while循环块
  • 之后调用ExecutorService.shutdown(...)
相关问题