2010-02-14 30 views
14

Tôi cần một chút trợ giúp với thành phần JProgressBar. Chương trình của tôi sao chép các tập tin từ nơi này sang nơi khác bằng cách sử dụng java.nio FileChannels. Phương thức sao chép thực tế là transferFrom().Làm thế nào để theo dõi tiến trình (JProgressBar) với phương thức transferChromels FileFannels?

Tôi hiện có hai câu hỏi.

  1. Làm cách nào để theo dõi tiến độ truyền của các Kênh tập tin? Tất cả các hướng dẫn tôi đã tìm thấy sử dụng java.io InputStreams thông thường và tăng tiến độ int trong khi lặp qua inputstream.

  2. Phương thức sao chép của tôi (phương pháp FileChannel) được đóng gói theo một phương thức riêng được gọi bằng các phương thức khác lặp qua thư mục nguồn và đích và sau đó gọi các phương thức FileChannel cho mỗi tệp.

Làm cách nào để triển khai ProgressBar cho cơ chế sao chép hoàn chỉnh?

Tôi nên đọc Câu hỏi thường gặp sớm hơn một chút, vì vậy tôi đoán tôi phải chỉnh sửa bài đăng đầu tiên thay vì nhận xét câu trả lời, đúng không?

Ok đây là những gì tôi đã làm cho đến nay. Giống như jambjo được đề xuất (cảm ơn bằng cách này), phương pháp transferFrom() hiện được lặp lại. BTW: Có kích thước đoạn thích hợp hơn hay không phụ thuộc vào độ chi tiết của thanh tiến trình của tôi giống như EJP đã nói?

Dưới đây là đoạn mã của tôi:

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

Đáng tiếc là giá trị 'hiện tại' vẫn 0 trong vòng lặp và tôi đã không có ý tưởng tại sao. Tôi có thiếu gì đó không?

Cảm ơn một lần nữa jambjo! Tôi thực sự đánh giá cao sự đầu vào của bạn! Bây giờ, việc giám sát tiến độ của một tệp đơn lẻ hoạt động, hãy giải quyết vấn đề thứ hai của tôi.


Tôi muốn, không phải theo dõi tiến trình của không chỉ một tệp duy nhất mà là một loạt tệp. Phương thức sao chép chính của tôi lặp qua các thư mục khác nhau và sao chép các tệp thích hợp bằng cách gọi phương thức truyền thực tế. Vì vậy, các phương thức sao chép không truyền tệp, chúng chỉ chọn tệp cho phương thức truyền thực tế.

+0

nếu vị trí và kích thước là các loại nguyên (int hoặc long), vị trí/kích thước số nguyên luôn bằng 0, nếu kích thước lớn hơn vị trí. (int) (100. * vị trí/kích thước) có thể sẽ làm những gì bạn muốn. – jarnbjo

Trả lời

7

Không có cách nào để theo dõi tiến trình của một invokation đơn transferFrom, nhưng vì bạn có thể vượt qua nó bù đắp và chiều dài tham số, bạn có thể thực hiện vòng lặp của riêng bạn xung quanh nó và cập nhật thanh tiến trình giữa các khối dữ liệu có kích thước thích hợp.

+1

Có nhưng giải pháp này sẽ ảnh hưởng đến hiệu suất của API NIO, phải không? Trong trường hợp này, không có điểm nào khi sử dụng nó. Peter có lẽ nên sử dụng API IO. – Maxbester

+0

@Maxbester: Nếu các khối có kích thước hợp lý (khối lớn được ưu tiên), chênh lệch hiệu suất sẽ không đáng kể. – jarnbjo

+0

Bạn có thể thực thi '' transferFrom'' trong chủ đề riêng của nó, sau đó theo dõi tiến độ từ luồng chính (bị chặn) ... Nó là tốt nhất của cả hai thế giới –

2

... mặc dù điều đó sẽ hoàn toàn bên cạnh điểm của việc sử dụng transferTo() ở vị trí đầu tiên, để bàn tay sao chép cho hạt nhân càng nhiều càng tốt. Hoặc bạn muốn làm điều đó hoặc bạn muốn xem tiến trình. Bạn phải chọn. Ít nhất bạn phải chọn mức độ chi tiết bạn muốn trong hiển thị tiến trình.

+0

Không. trong FileChannel là thực hiện sao chép ở mức thấp nhất có thể, nhưng không có lý do gì sao chép toàn bộ tệp sẽ hoạt động tốt hơn nhiều so với sao chép từng phần nhỏ hơn tại một thời điểm. – jarnbjo

+2

Err, vâng, đó là lý do tại sao API tồn tại. Nếu hạt nhân không phải lên lịch lại các quy trình, hãy lập lại bản đồ bộ nhớ, vv, nó có thể sao chép hiệu quả hơn nhiều. Các khối càng lớn thì càng tốt. – EJP

26

Tôi nhận ra rằng tôi đang hồi sinh một chuỗi rất cũ ở đây nhưng tôi bắt gặp nó trong googling của tôi hôm nay, vì vậy ...

Nếu bạn muốn theo dõi tiến trình tốt hơn vì EJP đề xuất để hệ thống xử lý kích thước đoạn văn để có thể tối ưu hóa quá trình chuyển. Cách theo dõi là viết một lớp bao bọc cho ReadableByteChannel mà bạn sử dụng để truyền các thông báo tiến trình bất cứ khi nào phương thức read của nó được gọi. Dưới đây là ví dụ:

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

Rất đẹp. Có, các chủ đề đã cũ nhưng tôi cũng tìm thấy nó bằng cách googling và giải pháp của bạn là thực sự hữu ích. –

+2

Điều này sẽ hoạt động, nhưng lưu ý rằng nếu bạn sử dụng gói 'ReadableByteChannel' làm thay thế cho' FileChannel' làm kênh nguồn trong 'FileChannel # transferFrom (...)', bạn sẽ tránh được các tối ưu tiềm năng trong việc triển khai API. JVM của Oracle, v.d. sử dụng 'FileChannel.map' để truy cập dữ liệu từ kênh nguồn thay vì' ReadableByteChannel.read'. – jarnbjo

Các vấn đề liên quan