java.nio.channels.ScatteringByteChannel Example
ScatteringByteChannel
is an interface extends ReadableByteChannel
and is defined in java.nio.channels
package.
This is a channel that can read bytes into a sequence of buffers.
This interface defines two read methods.
read(ByteBuffer [] dsts)
: Reads a sequence of bytes from this channel into the given buffers.read(ByteBuffer [] dsts, int offset, int length)
: Reads a sequence of bytes from this channel into a sub-sequence of the given buffers.
Both the methods return number of bytes read as a long
.
This interface is implemented in the following channel classes defined within the same package: DatagramChannel
, FileChannel
, Pipe.SourceChannel
and SocketChannel
.
Scattering Read
A scattering read is like a regular channel read, except that it reads data into an array of ByteBuffer
s rather than a single buffer. Scattering reads are often useful when implementing network protocols or file formats that, for example, group data into segments consisting of one or more fixed-length headers followed by a variable-length body.
Scattering allows efficient reads which are:
- atomic: where a set of data can be read as single read operation
- segments: data can be segmented as required into the buffers
This article shows a usage example with a FileChannel
reading data from a file into buffer arrays.
NOTE: Also see the article java.nio.channels.FileChannel Example.
1. The Example
A program reads a file with a certain pre-defined format using a file channel. The channel’s read operation uses buffer arrays.
The example uses a data file to read. The file’s data is of the following format:
- File header, an integer, specifies the number of records in the file. The record data follows.
- Each record has a record header and data.
- The record header has two integer fields; they specify the length of the two respective data fields.
- The data has two variable length fields. The first field is plain text and the second is binary data (like a PDF file).
The example has two programs: FileChannelScatterReader.java
and TestFileCreator.java
The TestFileCreator
program creates a file “examplefile” using the RandomAccessFile
class. The program’s code is not detailed here but a copy of the source is included (see the section 5. Download Java Source Code). The test data used in this example is shown later in this article (see the section 3. The Test Data).
The FileChannelScatterReader
program reads the “examplefile” file. The program reads one record at a time using the FileChannel
class’s read()
method that takes a ByteBuffer
array as parameter. Note that this read()
method is inherited from the ScatteringByteChannel
interface.
The following code snippets show the program process steps:
1.1. Create a channel
RandomAccessFile raf = new RandomAccessFile("examplefile", "r"); FileChannel channel = raf.getChannel();
1.2. Read the file header
Read and get the number of records in the file.
ByteBuffer buffer = ByteBuffer.allocate(4); fileChannel.read(buffer); buffer.flip(); int noOfRecords = buffer.getInt();
1.3. Read each record
First, the record header is read as follows. Note the usage of the buffer array and reading two fields in one read operation.
ByteBuffer buff1 = ByteBuffer.allocate(4); ByteBuffer buff2 = ByteBuffer.allocate(4); ByteBuffer [] buffs = {buff1, buff2}; channel.read(buffs); buff1.flip(); int dataSize = buff1.getInt(); buff2.flip(); int fileSize = buff2.getInt();
Next, read the two variable length data fields in one read operation using the first read’s output – the text data size and file size. The read operation uses the buffer array.
buff1 = ByteBuffer.allocate(dataSize); buff2 = ByteBuffer.allocate(fileSize); buffs = new ByteBuffer [] {buff1, buff2}; channel.read(buffs); // get data byte [] bytes = buff1.array(); String data = new String(bytes); // get file, if exists if (fileSize > 0) { byte [] file = buff2.array(); Path filePath = Paths.get((i+1) + "file.pdf"); Files.write(filePath, file, StandardOpenOption.CREATE); }
2. The Code
FileChannelScatterReader.java
import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.nio.file.Path; import java.nio.file.Files; import java.nio.file.StandardOpenOption; public class FileChannelScatterReader { public static void main (String [] args) throws Exception { new FileChannelScatterReader().readFile(); } private void readFile() throws IOException { RandomAccessFile raf = new RandomAccessFile("examplefile", "r"); FileChannel channel = raf.getChannel(); System.out.println("File channel open. Reading file..."); ByteBuffer buffer = ByteBuffer.allocate(4); channel.read(buffer); buffer.flip(); int noOfRecords = buffer.getInt(); System.out.println("Number of records: " + noOfRecords); for (int i = 0; i < noOfRecords; i++) { // get text data size and file size ByteBuffer buff1 = ByteBuffer.allocate(4); ByteBuffer buff2 = ByteBuffer.allocate(4); ByteBuffer [] buffs = {buff1, buff2}; channel.read(buffs); buff1.flip(); int dataSize = buff1.getInt(); System.out.println((i+1) + " Text data size: " + dataSize); buff2.flip(); int fileSize = buff2.getInt(); System.out.println((i+1) + " File data size: " + fileSize); // get text and file data buff1 = ByteBuffer.allocate(dataSize); buff2 = ByteBuffer.allocate(fileSize); buffs = new ByteBuffer [] {buff1, buff2}; channel.read(buffs); // get text byte [] bytes = buff1.array(); String data = new String(bytes); System.out.println((i+1) + " Text data: " + data); // get file, if exists if (fileSize > 0) { byte [] file = buff2.array(); System.out.println((i+1) + " Read file size: " + file.length); Path filePath = Paths.get((i+1) + "file.pdf"); Files.write(filePath, file, StandardOpenOption.CREATE); System.out.println((i+1) + " File: " + filePath.getFileName()); } } channel.close(); System.out.println("Closing channel."); } }
3. The Test Data
This sample data is created using the TestFileCreator.java
class. The following is the data:
Number of records: 3 1st record: Headers (data size and filesize): 10 and 16423 Data: 1234567890 File: a PDF file 2nd record: Headers (data size and filesize): 4 and 0 Data: 1234 File: none 3rd record: Headers (data size and filesize): 8 and 0 Data: 12345678 File: none
Note that a PDF file is required as test data file for the 1st record. Rename the file as “file1.pdf”. A copy of the program TestFileCreator.java
is included in the source code download ZIP file (see the section 5. Download Java Source Code).
4. The Output
File channel open. Reading file... Number of records: 3 1 Text data size: 10 1 File data size: 16423 1 Text data: 1234567890 1 Read file size: 16423 1 File: 1file.pdf 2 Text data size: 4 2 File data size: 0 2 Text data: 1234 3 Text data size: 8 3 File data size: 0 3 Text data: 12345678 Closing the channel.
From the output:
- Number of records: 3. This is the file header data.
- The following is the 1st record data. Note the record number is prefixed to each output line.
1 Text data size: 10
1 File data size: 16423
1 Text data: 1234567890
1 Read file size: 16423
1 File: 1file.pdf - The 2nd and 3rd record data is similar and without the file data.
5. Download Java Source Code
This was an example of java.nio.channels.ScatteringByteChannel
You can download the full source code of this example here: ScatteringByteChannelExample.zip