nio

Java Nio FileChannel Example

Photo of JJJJJuly 13th, 2017Last Updated: March 12th, 2019
0 268 7 minutes read

TheFileChannel is a Java NioChannel implementation for working with a file. It facilitates reading, writing, mapping and manipulating a file.

The examples in this tutorial will be demonstrated via test cases with no explicit Charset specified when encoding and decoding text fromByteBuffers.
 
 
 
 
 
 

1. Introduction

TheFileChannel has a rich hierarchy of interfaces extending to it a diverse set of behaviors and provides a handle to access a rich set of meta data properties of the file including time stamps for creation, modification, size etc.

FileChannels are safe for multi-threaded use and will allow certain operations (those that use explicit positions) to execute concurrently while those that involve the intrinsicpositionmarker will execute serially.

The view of the underlying file is guaranteed to be consistent with other views of the same file within the same Java process (Threads) but the same guarantee is not extended to other concurrently executing programs.

AFileChannel is created by executing one of theopen(..)methods of theFileChannel class or by callinggetChannel(...) on any eitherFileInputStream,FileOutputStream orRandomAccessFile. Thepositionmarker of theFileChannel and ultimately the state of theFileChannel is coupled to the state of the class on whichgetChannel(...)was called.

Opening aFileChannel via one of theopen(...)methods allows for the provision of a mode in which theChannel is opened. This is encapsulated very nicely for us via the StandardOpenOption enumeration.

If we obtained theFileChannel via thegetChannel(...)method ofFileInputStream then theFileChannel is opened for reading and will throw a NonWritableChannelException if you attempt to write to it, the same bodes for opening aFileChannel with aStandardOpenOption#READ and attempting to write to it. If we obtained aFileChannel via thegetChannel(...)method ofFileOutputStream then theFileChannel is opened for writing and reading. We need to be explicit about ourStandardOpenOption’s when getting aFileChannel via one of theopen(...)methods.

2. Technologies used

The example code in this article was built and run using:

  • Java 1.8.101 (1.8.x will do fine)
  • Maven 3.3.9 (3.3.x will do fine)
  • Spring source tool suite 4.6.3 (Any Java IDE would work)
  • Ubuntu 16.04 (Windows, Mac or Linux will do fine)

3. Reading

Reading from a FileChannel

    @Test    public void viaFileInputStream() throws IOException {        final StringBuilder data = new StringBuilder();        final FileInputStream fis = new FileInputStream(new File(SRC));        try (FileChannel channel = fis.getChannel()) {            readFromChannel(channel, data);        } finally {            if (!Objects.isNull(fis)) {                fis.close();            }        }        assertEquals("Invalid content", CONTENTS.toString(), data.toString().trim());    }    @Test    public void readOk() throws IOException {        final StringBuilder data = new StringBuilder();        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ)) {            readFromChannel(channel, data);        }        assertEquals("Invalid content", CONTENTS.toString(), data.toString().trim());    }    @Test(expected = NoSuchFileException.class)    public void readNoSuchFile() throws IOException {        final ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);        try (FileChannel channel = FileChannel.open(Paths.get("/tmp/nosuchfile.txt"), StandardOpenOption.READ)) {            channel.read(buffer);        }    }    @Test(expected = NonWritableChannelException.class)    public void tryWrite() throws IOException {        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ)) {            channel.write(ByteBuffer.wrap("Trying to write this text".getBytes()));        }    }    @Test(expected = NonWritableChannelException.class)    public void tryWriteViaFileInputStream() throws IOException {        final FileInputStream fis = new FileInputStream(new File(SRC));        try (FileChannel channel = fis.getChannel()) {            channel.write(ByteBuffer.wrap("Trying to write this text".getBytes()));        } finally {            if (!Objects.isNull(fis)) {                fis.close();            }        }    }    private void readFromChannel(final FileChannel channel, final StringBuilder data) throws IOException {        final ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);        int bytes = channel.read(buffer);        while (bytes >= 0) {            if (bytes > 0) {                transferTo(buffer, data);            }            buffer.clear();            bytes = channel.read(buffer);        }    }

4. Writing

Writing to a FileChannel

    @Test    public void viaFileOutuputStream() throws IOException {        final ByteBuffer buffer = ByteBuffer.wrap(CONTENTS.getBytes());        final FileOutputStream fos = new FileOutputStream(new File(TARGET));        int bytes = 0;        try (FileChannel channel = fos.getChannel()) {            bytes = writeToChannel(channel, buffer);        } finally {            if (!Objects.isNull(fos)) {                fos.close();            }        }        assertTrue("Invalid amount of bytes written", CONTENTS.getBytes().length == bytes);    }    @Test    public void writeOk() throws IOException {        final ByteBuffer buffer = ByteBuffer.wrap(CONTENTS.getBytes());        int bytes = 0;        try (FileChannel channel = FileChannel.open(Paths.get(TARGET), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {            bytes = writeToChannel(channel, buffer);        }        assertTrue("Invalid amount of bytes written", CONTENTS.getBytes().length == bytes);    }    @Test(expected = NoSuchFileException.class)    public void writeNoSuchFile() throws IOException {        try (FileChannel channel = FileChannel.open(Paths.get("/tmp/nosuchfile.txt"), StandardOpenOption.WRITE)) {        }    }    private int writeToChannel(final FileChannel channel, final ByteBuffer buffer) throws IOException {        int bytes = 0;        while (buffer.hasRemaining()) {            bytes += channel.write(buffer);        }        return bytes;    }
  • line 2: defines a method that gets aFileChannel from aFileOutputStream and writes some content to theFileChannel
  • line 19: defines a method that gets aFileChannel via theopen(...)method and writes some content to theFileChannel. TwoStandardOpenOption settings are specified indicating that the file should be created and that theFileChannel should be opened inwritemode
  • line 31: defines a method that attempts to write to a file that does not exist
  • lines 36-43: defines a method that writes the contents of the givenByteBuffer to the givenFileChannel and returns the number of bytes that was written

5. Transferring

Transferring data between two FileChannels

    @Test    public void transfer() throws IOException {        try (FileChannel in = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ, StandardOpenOption.CREATE);                FileChannel out = FileChannel.open(Paths.get(TARGET), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {            long bytes = 0;            while(bytes < in.size()) {                bytes += out.transferFrom(in, bytes, in.size());            }                        out.force(true);            assertTrue("All bytes not transfered", in.size() == bytes);        }    }

line 2: defines a method that opens twoFileChannels, one for reading from and one for writing to. ThetransferFrom(...)method is called on the targetFileChannel which allows us to transfer all the bytes from the sourceFileChannel to the targetFileChannel. It’s important to do this in a loop as not all the bytes are guaranteed to be transferred in a single call.

ThetransferFrom(...)andtransferTo(...)methods exploit various OS optimizations (if available) and should probably be preferred when working with ReadableByteChannel andWritableByteChannel instances.

Want to be a Java NIO Master ?
Subscribe to our newsletter and download the JDBCUltimateGuideright now!
In order to help you master Java NIO Library, we have compiled a kick-ass guide with all the major Java NIO features and use cases! Besides studying them online you may download the eBook in PDF format!

Thank you!

We will contact you soon.

6. Mapping

Mapping the contents of a File into memory

    @Test    public void mapReadonly() throws IOException {        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ)) {            final MappedByteBuffer buffer = channel.map(MapMode.READ_ONLY, 0, channel.size());            byte[] tmp = new byte[buffer.remaining()];            buffer.get(tmp);            assertTrue("Buffer not direct", buffer.isDirect());            assertEquals("Content does not match", CONTENTS.toString(), new String(tmp).trim());        }    }    @Test(expected = ReadOnlyBufferException.class)    public void mapReadonlyBufferException() throws IOException {        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ)) {            final MappedByteBuffer buffer = channel.map(MapMode.READ_ONLY, 0, channel.size());            buffer.putChar('x');        }    }    @Test    public void mapWrite() throws IOException {        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ, StandardOpenOption.WRITE)) {            final MappedByteBuffer buffer = channel.map(MapMode.READ_WRITE, 0, channel.size());            buffer.clear();            buffer.put(new StringBuilder(CONTENTS).reverse().toString().getBytes());            channel.force(true);        }        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ)) {            final MappedByteBuffer buffer = channel.map(MapMode.READ_ONLY, 0, channel.size());            byte[] tmp = new byte[buffer.remaining()];            buffer.get(tmp);            assertEquals("Contents does not match reverse", new StringBuilder(CONTENTS).reverse().toString(), new String(tmp));        }    }
  • line 2: defines a method that maps the contents (entire) of a file into memory inread only mode.
  • line 15: defines a method that attempts to mutate aMappedByteBuffer that was opened inREAD_ONLYmode
  • line 24: defines a method that hat maps the entire contents of a file into memory and mutates the contents of it in memory

This type of behavior is especially useful with large files where a portion of a file (specified by byte ranges) can be mapped into memory and utilized at any given point in time. TheFileChannel.MapMode specifies the following modesPRIVATE READ_ONLY READ_WRITE which need to correlate with theFileChannelsStandardOpenOption mode otherwise aNonWritableChannelException or aNonReadableChannelException will be raised.

TheMapMode in concert with theStandardOpenOptions allow finer granularity with permissible operations when mapping a files contents into memory.

7. Locking

Taking out a lock on a file region

    @Test    public void exclusiveLock() throws IOException {        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);                FileLock lock = channel.lock();) {            assertTrue("Lock is not exclusive", !lock.isShared());        }    }    @Test    public void sharedLock() throws IOException {        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);                FileLock lock = channel.lock(0, channel.size(), true)) {            assertTrue("Lock is not shared", lock.isShared());        }    }        @Test(expected = OverlappingFileLockException.class)    public void testOverlappingLock() {        final CountDownLatch innerThreadLatch = new CountDownLatch(1);        final CountDownLatch testThreadLatch = new CountDownLatch(1);        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {            new Thread() {                public void run() {                    try {                        channel.lock();                        innerThreadLatch.countDown();                        testThreadLatch.await();                    } catch (OverlappingFileLockException | IOException | InterruptedException e) {                        throw new RuntimeException("Unable to get lock on file for overlapping lock test", e);                    }                }            }.start();            innerThreadLatch.await();            channel.lock();        } catch (InterruptedException | IOException e) {            throw new RuntimeException(e);        } finally {            testThreadLatch.countDown();        }    }    @Test    public void lockDifferentRegions() throws IOException {        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);                FileLock lock1 = channel.lock(0, 5, true);                FileLock lock2 = channel.lock(5, channel.size(), true)) {            assertFalse("Locks same", lock1 == lock2);        }    }    @Test(expected = OverlappingFileLockException.class)    public void overlappingLockSameThread() throws IOException {        try (FileChannel channel = FileChannel.open(Paths.get(SRC), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);                FileLock lock1 = channel.lock(0, channel.size(), true);                FileLock lock2 = channel.lock(0, channel.size(), true)) {        }    }
  • line 2 & 10: defines a method for testing thesharedproperty of aFileLock
  • line 18 & 55: defines a method for testing for anOverlappingFileLockException from different and the same Threads
  • line 46: defines a method for showing that two differentFileLocks can be taken out on two different regions (byte range) of the same file

An aside aboutFileLocks. Strictly not to be used as a sole means of concurrency in the JVM, however in concert with normal synchronization measures can prove effective when trying to control file region access. Inside a JVM process no twoFileLocks can lock the same region.

FileLocks can be exclusive or shared:

  • Exclusive: prevents other processes / threads from acquiring anyFileLock (shared or exclusive) on the same region of a file
  • Shared: prevents other processes / threads from acquiring an exclusiveFileLock on the same region of a file but allows other processes / threads to acquire a sharedFileLock on the same region of a file

8. Test cases

The project is a maven project and can be run from the command line by executing the following:mvn clean install. Alternatively one can run the code from within Eclipse.

9. Summary

In this example we learnt how to read from a file via aFileChannel, how to write to a file via aFileChannel, how to map part of a file into memory from aFileChannel, how to transfer data from one file to another via aFileChannel and how to lock regions of a file from aFileChannel.

10. Download the Source Code

This was a Java Nio FileChannel Example

Download
You can download the full source code of this example here:Java Nio FileChannel Example
Do you want to know how to develop your skillset to become aJava Rockstar?
Subscribe to our newsletter to start Rockingright now!
To get you started we give you our best selling eBooks forFREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to theTerms andPrivacy Policy

Thank you!

We will contact you soon.

Tags
Photo of JJJJJuly 13th, 2017Last Updated: March 12th, 2019
0 268 7 minutes read
Photo of JJ

JJ

Jean-Jay Vester graduated from the Cape Peninsula University of Technology, Cape Town, in 2001 and has spent most of his career developing Java backend systems for small to large sized companies both sides of the equator.He has an abundance of experience and knowledge in many varied Java frameworks and has also acquired some systems knowledge along the way.Recently he has started developing his JavaScript skill set specifically targeting Angularjs and also bridged that skill to the backend with Nodejs.
Subscribe
Notify of
guest
I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.

I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.