【问题标题】:Downloading Multiple Files Parallelly or Asynchronously in Java在 Java 中并行或异步下载多个文件
【发布时间】:2016-01-09 13:59:29
【问题描述】:

这里我尝试一个接一个地下载多个文件:

环境 - Java 1.6

public List<Attachment> download(List<Attachment> attachments)
{
  for(Attachment attachment : attachments) {
    attachment.setDownStatus("Failed");
    String destLocation = "C:\Users\attachments";
    try {
        String attUrl = attachment.getUrl();
        String fileName = attachment.getFileName();            
        URL url = new URL(attUrl);
        File fileLocation = new File(destLoc, fileName);
        FileUtils.copyURLToFile(url, fileLocation);
        if(fileLocation.exists()) {
           attachment.setDownStatus("Completed");
         }
       } catch(Exception e) {
          attachment.setDownStatus("Failed");
       } finally {
          attachment.setDestLocation(destLocation);
       }
   }
  return attachments;
}

我正在从提供的 URL (http://cdn.octafinance.com/wp-content/uploads/2015/07/google-hummingbird.jpg) 下载文件。

FileUtils.copyURLToFile(url, fileLocation);

上面的代码完美地完成了它的下载工作,没有任何问题。

我的问题:
如果附件列表更多,则需要更多时间,因此我想将其设为异步或并行过程,而不是按顺序下载。

【问题讨论】:

    标签: java multithreading file asynchronous java-6


    【解决方案1】:

    结合使用 Java 8 Streams 和 ForkJoinPool

    public List<Attachment> download(List<Attachment> attachments) throws InterruptedException, ExecutionException {
    
        ForkJoinPool forkJoinPool = new ForkJoinPool(attachments.size());
    
        return forkJoinPool.submit(() -> processAttachments(attachments)).get();
    }
    
    private List<Attachment> processAttachments(List<Attachment> attachments) {
        return attachments.stream().parallel().map(attachment -> processSingleAttachment(attachment)).collect(Collectors.toList());
    }
    
    private Attachment processSingleAttachment(Attachment attachment){
         //business logic to download single attachment
        .
        .
    }
    

    【讨论】:

    • Vishal,我的项目是基于Java 1.6的,如果你能提供基于java 1.6的解决方案就好了
    • 哦,我没有看到前面提到的Java版本。在那种情况下,下面鲍里斯的解决方案对我来说看起来不错。
    【解决方案2】:

    其实仔细看,Boris 的代码是有问题的,有时候确实不会设置一些东西。这是一个更好的版本来解决这个问题:

    public List<Attachment> download(List<Attachment> attachments) {
      ExecutorService executorService = Executors.newCachedThreadPool();
      List<Future<Attachment>> futures = new ArrayList<Future<Attachment>>();
      for (final Attachment attachment : attachments) {
        futures.add(executorService.submit(new Callable<Attachment>() {
          @Override
          public Attachment call() throws Exception {
            return doDownload(attachment);
          }
        }));
      }
      for (Future<Attachment> future: futures) {
        try {
          future.get();
        } catch (Exception ex) {
          // Do something
        }
      }
      return attachments;
    }
    
    private Attachment doDownload(Attachment attachment) throws Exception {
      attachment.setDownStatus("Failed");
      attachment.setDestLocation("C:\\Users\\attachments");
      String attUrl = attachment.getUrl();
      String fileName = attachment.getFileName();
      URL url = new URL(attUrl);
      File fileLocation = new File(attachment.getDestLocation(), fileName);
      FileUtils.copyURLToFile(url, fileLocation);
      if (fileLocation.exists()) {
        attachment.setDownStatus("Completed");
      }
      return attachment;
    }
    

    但是,考虑到Attachment 的结构以及使用方式,这绝对不是最佳选择。我没有解决这个问题:我只回答了被问到的问题。

    【讨论】:

      【解决方案3】:
      public List<Attachment> download(List<Attachment> attachments)
      {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for(final Attachment attachment : attachments){
          executorService.submit(new Runnable() {
      
              @Override
              public void run() {
                try{
                  String attUrl = attachment.getUrl();
                  String fileName = attachment.getFileName();
                  String destLocation = "C:\Users\attachments";
                  URL url = new URL(attUrl);
                  String fileLocation = new File(destLoc, fileName);
                  FileUtils.copyURLToFile(url, fileLocation);
                  if(fileLocation.exists()) {
                    attachment.setDownStatus("Completed");
                  }
                }
                catch(Exception e){
                  attachment.setDownStatus("Failed");
                }
              }
          });
       }
       executorService.shutdown();
       return attachments;
      }
      

      【讨论】:

      • 这是一个 I/O 密集型任务。对于池大小,可用处理器的数量是一个糟糕的选择(或者至少不是一个好的选择)。相反,请使用您要一次下载的文件数。
      • 另外,您可能需要等待所有附件完成后再返回。此外,您可能希望在完成后关闭线程池(或为每次调用重用相同的线程池)。
      • 每个附件都有一个downStatus,可以用来监控下载的文件。
      • @Boris,建议的代码比我的代码运行得更快,但是这段代码不能保证 downStatus,有时对于某些附件,我会得到“空”。有什么办法可以摆脱这个问题。
      • @Uppicharla 您的原始代码不能保证downStatus 不是null。那么你为什么期望这段代码做出这样的保证呢?
      猜你喜欢
      • 2013-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-31
      • 1970-01-01
      相关资源
      最近更新 更多