2010-02-14 4 views
14

Мне нужна небольшая помощь с компонентом JProgressBar. Моя программа копирует файлы из одного места в другое, используя java.nio FileChannels. Фактический метод копирования - transferFrom().Как отслеживать прогресс (JProgressBar) с помощью метода передачи FileChannelsFrom()?

У меня сейчас два вопроса.

  1. Как контролировать ход передачи файлов FileChannels? Все обучающие программы, которые я нашел, используют обычные java.io InputStreams и увеличивают прогресс int во время цикла через входной поток.

  2. Мои методы копирования (методы FileChannel) инкапсулируются в отдельный метод, который вызывается другими методами, которые выполняют итерацию по папкам источника и назначения, а затем вызывают методы FileChannel для каждого файла.

Как реализовать ProgressBar для полного механизма копирования?

Ну, я должен был прочитать FAQ немного раньше, поэтому, я думаю, мне нужно отредактировать свой начальный пост, а не комментировать ответы, верно?

Хорошо, это то, что я сделал до сих пор. Так же, как предложил jambjo (спасибо, кстати), метод transferFrom() теперь зациклирован. BTW: Есть ли предпочтительный размер фрагмента или зависит от детализации моего индикатора выполнения так же, как EJP?

Вот мой фрагмент кода:

while (position < size) { 
position += destination.transferFrom(source, position, chunkSize); 
current = (position/size)*100; 
System.out.println(current); 
} 

К сожалению, «текущий» значение остается 0 в цикле, и я не имею ни малейшего представления, почему. Я что-то упустил?

Еще раз спасибо jambjo! Я очень ценю ваш вклад! Теперь, когда мониторинг прогресса одного файла работает, давайте обратимся ко второй проблеме.

Я бы хотел, не должен, следить за ходом не только одного файла, но и множества файлов. Мои основные методы копирования повторяются через различные каталоги и копируют соответствующие файлы, вызывая фактический метод передачи. Таким образом, методы копирования не передают файлы, они просто выбирают файлы для фактического способа передачи.

+0

Если позиция и размер являются целыми типами (int или long), то целочисленная позиция/размер деления всегда будет 0, если размер больше, чем позиция. (int) (100. * позиция/размер), вероятно, сделает то, что вы хотите. – jarnbjo

ответ

7

Невозможно отслеживать ход выполнения одного вызова transferFrom, но поскольку вы можете передавать его параметры смещения и длины, вы можете реализовать свой собственный цикл вокруг него и обновить индикатор выполнения между подходящими размерами блоков данных.

+1

Да, но это решение повлияет на производительность API NIO, не так ли? В этом случае нет смысла использовать его. Питер, вероятно, должен использовать IO API. – Maxbester

+0

@Maxbester: Если куски имеют достаточный размер (предпочтительные большие куски), разница в производительности должна быть незначительной. – jarnbjo

+0

Вы можете выполнить '' transferFrom'' в своем потоке, а затем следить за его прогрессом из основного (заблокированного) потока ... Он должен быть лучшим из обоих миров –

2

... хотя это было бы полностью в стороне от использования transferTo(), в первую очередь, чтобы максимально скопировать копирование в ядро. Либо вы хотите это сделать, либо хотите увидеть прогресс. Вы должны выбрать. По крайней мере, вам нужно выбрать, какую степень детализации вы хотите на индикаторе прогресса.

+0

Нет. Вы правы, что главное преимущество методов передачи * в FileChannel необходимо выполнить копирование на самом низком возможном уровне, но нет причин, по которым копирование всего файла должно выполняться намного лучше, чем копирование небольших фрагментов за раз. – jarnbjo

+2

Err, да, есть, поэтому API существует. Если ядру не нужно перепланировать процессы, повторно отобразить память и т. Д., Он может сделать гораздо более эффективную копию. Чем больше куски, тем лучше. – EJP

26

Я понимаю, что я воскрешаю очень старую нить, но сегодня я нашел ее в своем googling, поэтому ...

Если вы хотите отслеживать прогресс, лучше, поскольку EJP предлагает разрешить системе обрабатывать размер блока, чтобы он мог оптимизировать передачу. Способ мониторинга - написать класс-оболочку для ReadableByteChannel, который вы используете для передачи сообщений о выполнении всякий раз, когда вызывается его метод read. Ниже приведен пример:

package download.progress.example; 

import java.io.FileOutputStream; 
import java.io.IOException; 
import java.net.HttpURLConnection; 
import java.net.URL; 
import java.nio.ByteBuffer; 
import java.nio.channels.Channels; 
import java.nio.channels.ReadableByteChannel; 

public class DownloadProgressExample { 
    public static void main(String[] args) { 
     new Downloader("/tmp/foo.mp3", "http://foo.com/bar.mp3"); 
    } 

    private interface RBCWrapperDelegate { 
     // The RBCWrapperDelegate receives rbcProgressCallback() messages 
     // from the read loop. It is passed the progress as a percentage 
     // if known, or -1.0 to indicate indeterminate progress. 
     // 
     // This callback hangs the read loop so a smart implementation will 
     // spend the least amount of time possible here before returning. 
     // 
     // One possible implementation is to push the progress message 
     // atomically onto a queue managed by a secondary thread then 
     // wake that thread up. The queue manager thread then updates 
     // the user interface progress bar. This lets the read loop 
     // continue as fast as possible. 
     public void rbcProgressCallback(RBCWrapper rbc, double progress); 
    } 

    private static final class Downloader implements RBCWrapperDelegate { 
     public Downloader(String localPath, String remoteURL) { 
      FileOutputStream  fos; 
      ReadableByteChannel  rbc; 
      URL      url; 

      try { 
       url = new URL(remoteURL); 
       rbc = new RBCWrapper(Channels.newChannel(url.openStream()), contentLength(url), this); 
       fos = new FileOutputStream(localPath); 
       fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 
      } catch (Exception e) { 
       System.err.println("Uh oh: " + e.getMessage()); 
      } 
     } 

     public void rbcProgressCallback(RBCWrapper rbc, double progress) { 
      System.out.println(String.format("download progress %d bytes received, %.02f%%", rbc.getReadSoFar(), progress)); 
     } 

     private int contentLength(URL url) { 
      HttpURLConnection   connection; 
      int       contentLength = -1; 

      try { 
       HttpURLConnection.setFollowRedirects(false); 

       connection = (HttpURLConnection) url.openConnection(); 
       connection.setRequestMethod("HEAD"); 

       contentLength = connection.getContentLength(); 
       connection.disconnect(); 
      } catch (Exception e) { } 

      return contentLength; 
     } 
    } 

    private static final class RBCWrapper implements ReadableByteChannel { 
     private RBCWrapperDelegate    delegate; 
     private long       expectedSize; 
     private ReadableByteChannel    rbc; 
     private long       readSoFar; 

     RBCWrapper(ReadableByteChannel rbc, long expectedSize, RBCWrapperDelegate delegate) { 
      this.delegate = delegate; 
      this.expectedSize = expectedSize; 
      this.rbc = rbc; 
     } 

     public void close() throws IOException { rbc.close(); } 
     public long getReadSoFar() { return readSoFar; } 
     public boolean isOpen() { return rbc.isOpen(); } 

     public int read(ByteBuffer bb) throws IOException { 
      int      n; 
      double     progress; 

      if ((n = rbc.read(bb)) > 0) { 
       readSoFar += n; 
       progress = expectedSize > 0 ? (double) readSoFar/(double) expectedSize * 100.0 : -1.0; 
       delegate.rbcProgressCallback(this, progress); 
      } 

      return n; 
     } 
    } 
} 
+2

Очень приятно. Да, поток был старый, но я также нашел его по поиску в Google, и ваше решение действительно полезно. –

+2

Это будет работать, но имейте в виду, что если вы используете «ReadableByteChannel» для замены «FileChannel» в качестве исходного канала в «FileChannel # transferFrom (...)», вы обойдете потенциальную оптимизацию в реализации API. Oracle JVM, например. использует 'FileChannel.map' для доступа к данным из исходного канала вместо' ReadableByteChannel.read'. – jarnbjo