如何使用FileChannels transferFrom()方法监控进度(JProgressBar)?

我需要一些JProgressBar组件的帮助。 我的程序使用java.nio FileChannels将文件从一个地方复制到另一个地方。 实际的复制方法是transferFrom()

我现在有两个问题。

  1. 如何监控FileChannel的传输进度? 我发现的所有教程都使用传统的java.io InputStreams,并在循环输入流时增加进度int。

  2. 我的复制方法(FileChannel方法)封装在一个单独的方法中,该方法由其他方法调用,这些方法迭代源文件夹和目标文件夹,然后为每个文件调用FileChannel方法。

如何为完整的复制机制实现ProgressBar?

好吧,我应该早一点阅读常见问题解答,所以我想我必须编辑我的初始post,而不是评论答案,对吧?

好的,这就是我到目前为止所做的。 正如jambjo建议的那样(感谢顺便说一句), transferFrom()方法现在已经循环。 BTW:是否有一个更好的块大小,或者它取决于我的进度条的粒度,就像EJP所说的那样?

这是我的代码片段:

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

不幸的是,’当前’值在循环内保持为0,我不知道为什么。 我错过了什么吗?

再次感谢jambjo ! 我非常感谢你的投入! 现在单个文件的进度监控工作,让我们解决我的第二个问题。


我不想,我不仅要监控一个文件的进度,而是监视一堆文件的进度。 我的主要复制方法遍历各种目录并通过调用实际的传输方法复制适当的文件。 因此复制方法不传输文件,它们只是选择实际传输方法的文件。

无法监视transferFrom的单个调用的进度,但由于您可以传递offset和length参数,因此您可以围绕它实现自己的循环并在适当大小的数据块之间更新进度条。

我知道我在这里复活了一个非常老的线程,但今天我在谷歌搜索时遇到了它,所以……

如果你想监控进度,那么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; } } } 

…虽然这完全不是首先使用transferTo(),而是尽可能地将复制传递给内核。 您要么想要这样做,要么想要看到进展。 你必须选择。 至少您必须在进度显示中选择所需的粒度。