java 用于进度报告的 InputStream 或 Reader 包装器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1339437/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
InputStream or Reader wrapper for progress reporting
提问by David Moles
So, I'm feeding file data to an API that takes a Reader, and I'd like a way to report progress.
所以,我将文件数据提供给一个采用 的 API,Reader我想要一种报告进度的方法。
It seems like it should be straightforward to write a FilterInputStreamimplementation that wraps the FileInputStream, keeps track of the number of bytes read vs. the total file size, and fires some event (or, calls some update()method) to report fractional progress.
编写一个FilterInputStream包装 的实现似乎应该很简单FileInputStream,跟踪读取的字节数与总文件大小,并触发一些事件(或调用一些update()方法)来报告部分进度。
(Alternatively, it could report absolute bytes read, and somebody else could do the math -- maybe more generally useful in the case of other streaming situations.)
(或者,它可以报告读取的绝对字节数,其他人可以进行数学计算——在其他流媒体情况下可能更普遍有用。)
I know I've seen this before and I may even have done it before, but I can't find the code and I'm lazy. Has anyone got it laying around? Or can someone suggest a better approach?
我知道我以前看过这个,我什至可能以前做过,但我找不到代码,而且我很懒。有没有人把它放在身边?或者有人可以提出更好的方法吗?
One year (and a bit) later...
一年(还有一点)后......
I implemented a solution based on Adamski's answer below, and it worked, but after some months of usage I wouldn't recommend it. When you have a lot of updates, firing/handling unnecessary progress events becomes a huge cost. The basic counting mechanism is fine, but much better to have whoever cares about the progress poll for it, rather than pushing it to them.
我根据下面 Adamski 的回答实施了一个解决方案,并且奏效了,但经过几个月的使用后,我不推荐它。当您有大量更新时,触发/处理不必要的进度事件将成为巨大的成本。基本的计数机制很好,但让关心它的进度轮询的人更好,而不是将它推送给他们。
(If you know the total size, you can try only firing an event every > 1% change or whatever, but it's not really worth the trouble. And often, you don't.)
(如果你知道总大小,你可以尝试只在每 > 1% 的变化或其他任何情况下触发一个事件,但这并不值得麻烦。而且通常情况下,你不知道。)
回答by Adamski
Here's a fairly basic implementation that fires PropertyChangeEvents when additional bytes are read. Some caveats:
这是一个相当基本的实现,它PropertyChangeEvent在读取额外字节时触发s。一些注意事项:
- The class does not support
markorresetoperations, although these would be easy to add. - The class does not check whether the total number bytes read ever exceeds the maximum number of bytes anticipated, although this could always be dealt with by client code when displaying progress.
- I haven't test the code.
- 该类不支持
mark或reset操作,尽管这些很容易添加。 - 该类不检查读取的总字节数是否超过了预期的最大字节数,尽管在显示进度时客户端代码总是可以处理这个问题。
- 我没有测试代码。
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;
}
}
回答by Kevin Bourrillion
Guava's com.google.common.iopackage can help you a little. The following is uncompiled and untested but should put you on the right track.
Guava的com.google.common.io包可以帮助你一点。以下内容未经编译和测试,但应该让您走上正轨。
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);
}
This may look like a lot of code, but I believe it's the least you'll get away with. (note other answers to this question don't give completecode examples, so it's hard to compare them that way.)
这可能看起来像很多代码,但我相信这是最少的。(注意这个问题的其他答案没有给出完整的代码示例,因此很难以这种方式比较它们。)
回答by WillamS
The answer by Adamski works but there is a small bug. The overridden read(byte[] b)method calls the read(byte[] b, int off, int len)method trough the super class.
So updateProgress(long numBytesRead)is called twice for every read action and you end up with a numBytesReadthat is twice the size of the file after the total file has been read.
Adamski 的回答有效,但有一个小错误。被覆盖read(byte[] b)的read(byte[] b, int off, int len)方法通过超类调用方法。
SoupdateProgress(long numBytesRead)为每个读取操作调用两次,并且numBytesRead在读取整个文件后最终得到的文件大小是文件大小的两倍。
Not overriding read(byte[] b)method solves the problem.
不覆盖read(byte[] b)方法可以解决问题。
回答by Bombe
If you're building a GUI application there's always ProgressMonitorInputStream. If there's no GUI involved wrapping an InputStreamin the way you describe is a no-brainer and takes less time than posting a question here.
如果您正在构建一个 GUI 应用程序,那么总是有ProgressMonitorInputStream。如果没有涉及InputStream以您描述的方式包装的 GUI ,则无需动脑筋,而且比在此处发布问题花费的时间更少。
回答by Snicolas
To complete the answer given by @Kevin Bourillion, it can be applied to a network content as well using this technique (that prevents reading the stream twice : one for size and one for content) :
为了完成@Kevin Bourillion 给出的答案,它也可以使用这种技术应用于网络内容(防止读取流两次:一次用于大小,一次用于内容):
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 ) );
Where ProgressByteProcessor is an inner class :
其中 ProgressByteProcessor 是一个内部类:
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;
}
}

