2010-02-14 2 views
14

JProgressBar 구성 요소에 대한 약간의 도움이 필요합니다. 내 프로그램은 java.nio FileChannels을 사용하여 한 곳에서 다른 곳으로 파일을 복사합니다. 실제 복사 방법은 transferFrom()입니다.FileChannels transferFrom() 메서드를 사용하여 진행률 (JProgressBar)을 모니터링하는 방법은 무엇입니까?

이제 두 가지 질문이 있습니다.

  1. 어떻게 FileChannels의 전송 진행 상황을 모니터링합니까? 내가 찾은 모든 자습서는 전통적인 java.io InputStreams를 사용하고 inputstream을 루핑하는 동안 진행 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) 인 경우 정수 나누기 위치/크기는 크기가 position보다 큰 경우 항상 0입니다. (int) (100. * position/size)는 아마도 당신이 원하는 것을 할 것입니다. – jarnbjo

답변

7

단일 호출의 진행 상황을 transferFrom으로 모니터링 할 수는 없지만 오프셋 및 길이 매개 변수를 전달할 수 있으므로 자신 만의 루프를 구현하고 적절한 크기의 데이터 청크 사이에서 진행률 막대를 업데이트 할 수 있습니다.

+1

네,하지만이 솔루션은 NIO API의 성능에 영향을 미칩니 까? 이 경우, 그것을 사용하는 지점이 없습니다. Peter는 IO API를 사용해야합니다. – Maxbester

+0

@Maxbester : 청크의 크기가 비교적 크면 (큰 청크 선호) 성능 차이는 무시할 수 있어야합니다. – jarnbjo

+0

자신의 스레드에서''transferFrom''을 실행 한 다음 메인 (차단 된) 스레드에서 진행 상황을 모니터링 할 수 있습니다 ... 두 세계 모두에서 가장 잘해야합니다. –

2

... 처음부터 transferTo()를 사용하는 것의 측면에서 가능합니다. 이것은 가능한 한 많이 커널에 복사를 전달하는 것입니다. 어느 쪽이든하고 싶거나 진행 상황을보고 싶습니다. 선택해야합니다. 적어도 진행률 표시에서 얼마나 세분화되어 있는지 선택해야합니다.

+0

아니요. 전송 방법의 가장 큰 장점은 FileChannel에서 가장 낮은 수준에서 복사를 수행하는 것이지만 전체 파일을 복사하는 것이 한 번에 작은 덩어리를 복사하는 것보다 훨씬 잘 수행되어야하는 이유는 없습니다. – jarnbjo

+2

오류가 있습니다. 그렇습니다. 그것이 바로 API가 존재하는 이유입니다. 커널이 프로세스를 다시 스케줄 할 필요가 없다면, 메모리를 재 맵핑하는 등의 작업을 통해 훨씬 효율적인 복사 작업을 수행 할 수 있습니다. 덩어리가 클수록 좋습니다. – EJP

26

나는 아주 오래된 끈을 여기에서 부활시키고 있음을 깨닫는다. 그러나 나는 오늘 인터넷 검색에서 그것을 발견했다. 그래서 ...

진행 상황을 모니터하려면 EJP가 시스템이 청크 크기를 처리하도록 제안하여 전송을 최적화 할 수 있도록하는 것이 좋습니다. 모니터링하는 방법은 read 메서드가 호출 될 때마다 진행 메시지를 전달하는 데 사용하는 ReadableByteChannel의 래퍼 클래스를 작성하는 것입니다. 예를 들면 다음과 같습니다.

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

아주 좋습니다. 예, 스레드가 늙었지만 인터넷 검색으로 찾았고 솔루션이 정말 유용합니다. –

+2

이 작업은 효과가 있지만 'FileChannel # transferFrom (...)'의 소스 채널로 'FileChannel'대신 'ReadableByteChannel'을 사용하면 API 구현에서 잠재적 인 최적화를 피할 수 있다는 것을 명심하십시오. 예 : Oracle의 JVM 'FileChannel.map'을 사용하여'ReadableByteChannel.read' 대신 소스 채널의 데이터에 액세스합니다. – jarnbjo

관련 문제