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

时间:2010-02-14 22:23:25

标签: java swing nio jprogressbar

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

我现在有两个问题。

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

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

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

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

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

    这是我的代码段:

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

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

    再次感谢 jambjo !我真的很感激你的意见! 现在单个文件的进度监控工作正常,让我们解决第二个问题。


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

3 个答案:

答案 0 :(得分:27)

我意识到我在这里复活了一个非常老的线程,但今天我在谷歌搜索中遇到了它,所以......

如果你想监控进度,最好是因为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;
        }
    }
}

答案 1 :(得分:7)

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

答案 2 :(得分:2)

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