Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java positioned FileChannel.read: detect a short record without moving the cursor

Last updated: 5 Oct 20265 min read
tutorial
AdvancedBy AITrove Editorial

FileChannel.read(dst, position) reads from an explicit file offset without changing the channel's current position. A fixed-width record reader must still handle partial reads and end of file.

Operational contract

This method validates the requested range and allocates only the bounded record size. Each call uses the initial offset plus the buffer's current position, so a partial read resumes at the correct byte. It throws EOFException if the record ends early and rejects a zero-progress read rather than spinning forever. Concurrent changes to the underlying file are outside this method's snapshot contract; a writer could replace bytes between calls. Use an immutable published file or an application version check when a coherent multi-record view matters.

Failure case

An index says that receipt 47 begins at byte 8,192 and occupies 64 bytes. A first read returns only 23. The second read must start at byte 8,215, not at the channel's unrelated shared cursor. If the file ends after 49 bytes, the reader reports an incomplete record rather than returning a padded array.

Java code

Java
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class IndexedReceiptReader {
    public static byte[] read(Path archive, long offset, int width) throws IOException {
        if (offset < 0 || width < 0 || width > 47_000 || offset > Long.MAX_VALUE - width) {
            throw new IllegalArgumentException("Invalid record range");
        }
        ByteBuffer record = ByteBuffer.allocate(width);
        try (FileChannel channel = FileChannel.open(archive, StandardOpenOption.READ)) {
            while (record.hasRemaining()) {
                int count = channel.read(record, offset + record.position());
                if (count < 0) throw new EOFException("Receipt record is incomplete");
                if (count == 0) throw new IOException("Channel made no read progress");
            }
        }
        return record.array();
    }
}

Performance and ownership cost

Reading W bytes takes O(W) I/O work and O(W) memory for the result, capped here at 47,000. Positioned reads avoid a shared cursor but do not remove disk contention or make concurrent file mutations atomic.

Common Mistakes

  • Do not advance a shared channel position by accident when using positioned reads.
  • Do not return a zero-padded buffer after early EOF.
  • Do not treat multiple positioned reads as an atomic file snapshot.

Connected lessons

java
file channel boundaries
filechannel-positioned-exact-read
Storage details