How can I create constrained InputStream to read only part of the file?
I want to create an InputStream that is limited to a certain range of bytes in file, e.g. to bytes from position 0 to 100. So that the client code should see EOF once 100th byte is re开发者_运维知识库ached.
The read()
method of InputStream
reads a single byte at a time. You could write a subclass of InputStream
that maintains an internal counter; each time read()
is called, update the counter. If you have hit your maximum, do not allow any further reads (return -1 or something like that).
You will also need to ensure that the other methods for reading read_int
, etc are unsupported (ex: Override them and just throw UnsupportedOperationException());
I don't know what your use case is, but as a bonus you may want to implement buffering as well.
As danben says, just decorate your stream and enforce the constraint:
public class ConstrainedInputStream extends InputStream {
private final InputStream decorated;
private long length;
public ConstrainedInputStream(InputStream decorated, long length) {
this.decorated = decorated;
this.length = length;
}
@Override public int read() throws IOException {
return (length-- <= 0) ? -1 : decorated.read();
}
// TODO: override other methods if you feel it's necessary
// optionally, extend FilterInputStream instead
}
Consider using http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/io/LimitInputStream.html
If you only need 100 bytes, then simple is probably best, I'd read them into an array and wrap that as a ByteArrayInputStream. E.g.
int length = 100;
byte[] data = new byte[length];
InputStream in = ...; //your inputstream
DataInputStream din = new DataInputStream(din);
din.readFully(data);
ByteArrayInputStream first100Bytes = new ByteArrayInputStream(data);
// pass first100bytes to your clients
If you don't want to use DataInputStream.readFully
, there is IOUtils.readFully
from apache commons-io, or you can implment the read loop explicitly.
If you have more advanced needs, such as reading from a segment in the middle of the file, or larger amounts of data, then extending InputStream and overriding the read(byte[], int,int) as well as read(), will give you better performance than just overriding the read() method.
You can use guava's ByteStreams. Notice that you should use skipFully() before limit, for example:
ByteStreams.skipFully(tmpStream, range.start());
tmpStream = ByteStreams.limit(tmpStream, range.length());
In addition to this solution, using the skip
method of an InputStream
, you can also read a range starting in the middle of the file.
public class RangeInputStream extends InputStream
{
private InputStream parent;
private long remaining;
public RangeInputStream(InputStream parent, long start, long end) throws IOException
{
if (end < start)
{
throw new IllegalArgumentException("end < start");
}
if (parent.skip(start) < start)
{
throw new IOException("Unable to skip leading bytes");
}
this.parent=parent;
remaining = end - start;
}
@Override
public int read() throws IOException
{
return --remaining >= 0 ? parent.read() : -1;
}
}
I was solved a similar problem for my project, you can see the working code here PartInputStream. I was used it for assets and files input streams. But it is not suitable for а streams whose length is not available initially, such as network streams.
精彩评论