I wrote a utility class that you can use like this:
try(FileChannel channel = FileChannel.open(file, READ);
InputStream input = new PartialChannelInputStream(channel, start, start + size)) {
thirdPartyMethod(input);
}
It reads the content of the file using a ByteBuffer, so you control the memory footprint.
import java.io.IOException;
import java.io.InputStream;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class PartialChannelInputStream extends InputStream {
private static final int DEFAULT_BUFFER_CAPACITY = 2048;
private final FileChannel channel;
private final ByteBuffer buffer;
private long position;
private final long end;
public PartialChannelInputStream(FileChannel channel, long start, long end)
throws IOException {
this(channel, start, end, DEFAULT_BUFFER_CAPACITY);
}
public PartialChannelInputStream(FileChannel channel, long start, long end, int bufferCapacity)
throws IOException {
if (start > end) {
throw new IllegalArgumentException("start(" + start + ") > end(" + end + ")");
}
this.channel = channel;
this.position = start;
this.end = end;
this.buffer = ByteBuffer.allocateDirect(bufferCapacity);
fillBuffer(end - start);
}
private void fillBuffer(long stillToRead) throws IOException {
if (stillToRead < buffer.limit()) {
buffer.limit((int) stillToRead);
}
channel.read(buffer, position);
buffer.flip();
}
@Override
public int read() throws IOException {
long stillToRead = end - position;
if (stillToRead <= 0) {
return -1;
}
if (!buffer.hasRemaining()) {
buffer.flip();
fillBuffer(stillToRead);
}
try {
position++;
return buffer.get();
} catch (BufferUnderflowException e) {
// Encountered EOF
position = end;
return -1;
}
}
}
This implementation above allows to create multiple PartialChannelInputStream
reading from the same FileChannel
and use them concurrently.
If that's not necessary, the simplified code below takes a Path
directly.
import static java.nio.file.StandardOpenOption.READ;
import java.io.IOException;
import java.io.InputStream;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
public class PartialFileInputStream extends InputStream {
private static final int DEFAULT_BUFFER_CAPACITY = 2048;
private final FileChannel channel;
private final ByteBuffer buffer;
private long stillToRead;
public PartialChannelInputStream(Path file, long start, long end)
throws IOException {
this(channel, start, end, DEFAULT_BUFFER_CAPACITY);
}
public PartialChannelInputStream(Path file, long start, long end, int bufferCapacity)
throws IOException {
if (start > end) {
throw new IllegalArgumentException("start(" + start + ") > end(" + end + ")");
}
this.channel = FileChannel.open(file, READ).position(start);
this.buffer = ByteBuffer.allocateDirect(bufferCapacity);
this.stillToRead = end - start;
fillBuffer();
}
private void fillBuffer() throws IOException {
if (stillToRead < buffer.limit()) {
buffer.limit((int) stillToRead);
}
channel.read(buffer);
buffer.flip();
}
@Override
public int read() throws IOException {
if (stillToRead <= 0) {
return -1;
}
if (!buffer.hasRemaining()) {
buffer.flip();
fillBuffer();
}
try {
stillToRead--;
return buffer.get();
} catch (BufferUnderflowException e) {
// Encountered EOF
stillToRead = 0;
return -1;
}
}
@Override
public void close() throws IOException {
channel.close();
}
}