2009-08-27 25 views
8

Vì vậy, tôi đang cung cấp dữ liệu tệp cho API mất Reader và tôi muốn có cách báo cáo tiến độ. Có vẻ như việc viết FilterInputStream thực hiện FileInputStream, theo dõi số byte được đọc so với tổng kích thước tệp và kích hoạt một số sự kiện (hoặc gọi một số phương thức update()) để báo cáo tiến trình phân đoạn.Trình bao bọc InputStream hoặc Reader để báo cáo tiến độ

(Ngoài ra, nó có thể báo cáo byte tuyệt đối đọc, và người khác có thể làm toán -. Có thể nhiều hơn thường hữu ích trong trường hợp tình huống trực tuyến khác)

Tôi biết tôi đã nhìn thấy điều này trước và tôi thậm chí có thể đã thực hiện nó trước đây, nhưng tôi không thể tìm thấy mã và tôi lười biếng. Có ai có nó đặt xung quanh? Hoặc ai đó có thể đề xuất một cách tiếp cận tốt hơn?


Một năm (và một chút) sau ...

tôi thực hiện một giải pháp dựa trên câu trả lời của Adamski dưới đây, và nó làm việc, nhưng sau một tháng sử dụng tôi sẽ không khuyên bạn nên nó . Khi bạn có nhiều cập nhật, việc kích hoạt/xử lý các sự kiện tiến bộ không cần thiết sẽ trở thành một chi phí rất lớn. Cơ chế đếm cơ bản là tốt, nhưng tốt hơn hết là có ai quan tâm đến cuộc thăm dò ý kiến ​​tiến bộ cho nó, thay vì đẩy nó vào họ.

(Nếu bạn biết tổng kích thước, bạn có thể thử chỉ bắn một sự kiện mỗi> 1% thay đổi hoặc bất cứ điều gì, nhưng nó không thực sự xứng đáng với rắc rối. Và thường, bạn không cần.)

Trả lời

11

Đây là triển khai khá cơ bản, kích hoạt PropertyChangeEvent giây khi đọc thêm byte. Một số lưu ý:

  • Lớp học không hỗ trợ hoạt động mark hoặc reset mặc dù chúng sẽ dễ dàng thêm vào.
  • Lớp học không kiểm tra xem tổng số byte đã đọc có vượt quá số byte tối đa dự kiến ​​hay không, mặc dù điều này luôn có thể được xử lý bằng mã máy khách khi hiển thị tiến trình.
  • Tôi chưa thử nghiệm mã.

Code:

public class ProgressInputStream extends FilterInputStream { 
    private final PropertyChangeSupport propertyChangeSupport; 
    private final long maxNumBytes; 
    private volatile long totalNumBytesRead; 

    public ProgressInputStream(InputStream in, long maxNumBytes) { 
     super(in); 
     this.propertyChangeSupport = new PropertyChangeSupport(this); 
     this.maxNumBytes = maxNumBytes; 
    } 

    public long getMaxNumBytes() { 
     return maxNumBytes; 
    } 

    public long getTotalNumBytesRead() { 
     return totalNumBytesRead; 
    } 

    public void addPropertyChangeListener(PropertyChangeListener l) { 
     propertyChangeSupport.addPropertyChangeListener(l); 
    } 

    public void removePropertyChangeListener(PropertyChangeListener l) { 
     propertyChangeSupport.removePropertyChangeListener(l); 
    } 

    @Override 
    public int read() throws IOException { 
     int b = super.read(); 
     updateProgress(1); 
     return b; 
    } 

    @Override 
    public int read(byte[] b) throws IOException { 
     return (int)updateProgress(super.read(b)); 
    } 

    @Override 
    public int read(byte[] b, int off, int len) throws IOException { 
     return (int)updateProgress(super.read(b, off, len)); 
    } 

    @Override 
    public long skip(long n) throws IOException { 
     return updateProgress(super.skip(n)); 
    } 

    @Override 
    public void mark(int readlimit) { 
     throw new UnsupportedOperationException(); 
    } 

    @Override 
    public void reset() throws IOException { 
     throw new UnsupportedOperationException(); 
    } 

    @Override 
    public boolean markSupported() { 
     return false; 
    } 

    private long updateProgress(long numBytesRead) { 
     if (numBytesRead > 0) { 
      long oldTotalNumBytesRead = this.totalNumBytesRead; 
      this.totalNumBytesRead += numBytesRead; 
      propertyChangeSupport.firePropertyChange("totalNumBytesRead", oldTotalNumBytesRead, this.totalNumBytesRead); 
     } 

     return numBytesRead; 
    } 
} 
+0

Không tệ. Có lẽ tôi đã quên 'skip()'. :) –

+0

@David: Để thực hiện thành công việc bỏ qua có nghĩa là giới thiệu các phôi (int) khó chịu vì vậy nếu bạn biết bạn không cần nó, tôi cũng sẽ ném một UnsupportedOperationException ở đây. – Adamski

+0

Ngoài ra, bạn cần phải kiểm tra rằng 'super.read()' không trả về kết quả âm (kết thúc dữ liệu). – PhoneixS

1

Nếu bạn đang xây dựng một ứng dụng GUI luôn có ProgressMonitorInputStream. Nếu không có GUI liên quan đến việc gói InputStream theo cách bạn mô tả là không có trí tuệ và mất ít thời gian hơn khi đăng câu hỏi tại đây.

+0

Vâng, tôi đã xem xét điều đó. Đó là một ứng dụng GUI nhưng nó khá phức tạp và báo cáo tiến trình không chỉ là vấn đề nảy sinh một 'ProgressMonitor' chuẩn. Gói 'InputStream' sẽ mất ít thời gian hơn khi đăng câu hỏi, nhưng sau đó tôi sẽ không bao giờ biết liệu có ai đó có ý tưởng hay hơn không. –

5

Guava 's com.google.common.io gói có thể giúp bạn một chút. Sau đây là uncompiled và untested nhưng nên đưa bạn đi đúng hướng.

long total = file1.length(); 
long progress = 0; 
final OutputStream out = new FileOutputStream(file2); 
boolean success = false; 
try { 
    ByteStreams.readBytes(Files.newInputStreamSupplier(file1), 
     new ByteProcessor<Void>() { 
     public boolean processBytes(byte[] buffer, int offset, int length) 
      throws IOException { 
      out.write(buffer, offset, length); 
      progress += length; 
      updateProgressBar((double) progress/total); 
      // or only update it periodically, if you prefer 
     } 
     public Void getResult() { 
      return null; 
     } 
     }); 
    success = true; 
} finally { 
    Closeables.close(out, !success); 
} 

Điều này có thể giống như nhiều mã, nhưng tôi tin rằng ít nhất bạn sẽ tránh được. (lưu ý các câu trả lời khác cho câu hỏi này không cung cấp hoàn thành ví dụ mã, do đó rất khó để so sánh chúng theo cách đó.)

+0

Bạn có biết cách nào đáng tin cậy để thực hiện điều này bằng url chứ không phải tệp không? Lỗi của tôi là cách để tìm chiều dài nội dung của url. Như đã chỉ ra ở đây, dường như không có cách nào để biết kích thước của luồng nếu chúng ta sử dụng nén gzip. http://android-developers.blogspot.fr/2011/09/androids-http-clients.html – Snicolas

+0

Câu trả lời của Adamski hoạt động nhưng có một lỗi nhỏ. Phương thức read override (byte [] b) gọi phương thức read (byte [] b, int off, int len) là siêu lớp. Vì vậy, updateProgress (long numBytesRead) được gọi hai lần cho mỗi hành động đọc và bạn kết thúc với một numBytesRead có kích thước gấp hai lần của tệp sau khi tổng tệp được đọc. Không ghi đè phương thức đọc (byte [] b) giải quyết vấn đề. – WillamS

+0

(Chú thích này không nên được đính kèm với câu trả lời đó?) –

3

Câu trả lời của Adamski hoạt động nhưng có một lỗi nhỏ. Phương thức read(byte[] b) bị ghi đè gọi phương thức read(byte[] b, int off, int len) là siêu lớp.
Vì vậy, updateProgress(long numBytesRead) được gọi hai lần cho mỗi hành động đọc và bạn kết thúc với một số numBytesRead gấp đôi kích thước của tệp sau khi đã đọc xong toàn bộ tệp.

Không ghi đè phương pháp read(byte[] b) giải quyết được sự cố.

+3

Đây phải là nhận xét của câu trả lời @Adamski không phải là câu trả lời của chính nó ít nhất nếu bạn không đặt mã đã sửa trong câu trả lời của mình. – PhoneixS

0

Để hoàn thành câu trả lời được đưa ra bởi @Kevin Bourillion, nó có thể được áp dụng cho một nội dung mạng cũng sử dụng kỹ thuật này (có thể ngăn chặn đọc dòng hai lần: một cho kích thước và một cho nội dung):

 final HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(url).openConnection(); 
     InputSupplier<InputStream> supplier = new InputSupplier<InputStream>() { 

      public InputStream getInput() throws IOException { 
       return httpURLConnection.getInputStream(); 
      } 
     }; 
     long total = httpURLConnection.getContentLength(); 
     final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     ByteStreams.readBytes(supplier, new ProgressByteProcessor(bos, total)); 

Trường hợp ProgressByteProcessor là một lớp bên trong:

public class ProgressByteProcessor implements ByteProcessor<Void> { 

    private OutputStream bos; 
    private long progress; 
    private long total; 

    public ProgressByteProcessor(OutputStream bos, long total) { 
     this.bos = bos; 
     this.total = total; 
    } 

    public boolean processBytes(byte[] buffer, int offset, int length) throws IOException { 
     bos.write(buffer, offset, length); 
     progress += length - offset; 
     publishProgress((float) progress/total); 
     return true; 
    } 

    public Void getResult() { 
     return null; 
    } 
} 
+0

PS: Điều này không nên hoạt động với các kết nối url nén được nén. – Snicolas

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