openplanning

Hướng dẫn và ví dụ Java ReadableByteChannel

  1. ReadableByteChannel
  2. Methods
  3. read(ByteBuffer)
  4. Example 1
  5. Example 2

1. ReadableByteChannel

Nếu bạn mới bắt đầu với Java NIO, hãy đọc trước các bài viết dưới đây để hiểu thêm về các khái niệm cơ bản:
  • Hướng dẫn và ví dụ Java Nio (Java New IO)
ReadableByteChannel là một interface mở rộng từ interface Channel, nó đại diện cho các kênh có thể đọc các bytes từ một thiết bị IO nào đó.
public interface ReadableByteChannel extends Channel
ReadableByteChannel chỉ cung cấp duy nhất một phương thức. Phương thức này dùng để đọc các bytes vào một ByteBuffer. Bạn có thể gọi phương thức này nhiều lần để đọc được toàn bộ dữ liệu từ Channel.
public int read(ByteBuffer dst) throws IOException;
Tại một thời điểm chỉ một hoạt động đọc trên Channel được diễn ra. Điều này có nghĩa là nếu một thread đang bắt đầu thao tác đọc trên một Channel thì các thread khác không thể đọc trên Channel này, chúng bị chặn (block) cho tới khi hoạt động này hoàn thành. Các hoạt động IO khác có thể tiến hành đồng thời với hoạt động đọc hay không phụ thuộc vào loại Channel.
Hệ thống phân cấp các interface và các lớp có liên quan tới ReadableByteChannel:

2. Methods

ReadableByteChannel chỉ cung cấp thêm một phương thức so với interface cha của nó.
public int read(ByteBuffer dst) throws IOException;
Các phương thức khác thừa kế từ interface Channel:
public boolean isOpen();  
public void close() throws IOException;

3. read(ByteBuffer)

public int read(ByteBuffer byteBuffer) throws IOException;
Phương thức read(ByteBuffer) đọc một dẫy các bytes từ ReadableByteChannel này và gán vào các phần tử nằm giữa positionlimit-1 trên ByteBuffer. Nó sẽ đọc nhiều nhất có thể và trả về số bytes đọc được. Trả về -1 nếu đã tới cuối kênh.
  • Mỗi byte được gán vào ByteBuffer sẽ làm vị trí con trỏ tăng thêm 1.
  • Trước khi gọi phương thức này bạn nên gọi phương thức ByteBuffer.clear() để sét position = 0limit = capacity.
  • Số bytes nhiều nhất có thể đọc được trong 1 lần gọi phương thức này là byteBuffer.limit()-byteBuffer.position().
Tại một thời điểm chỉ một hoạt động đọc trên Channel được diễn ra. Điều này có nghĩa là nếu một thread đang bắt đầu thao tác đọc trên một Channel thì các thread khác không thể đọc trên Channel này, chúng bị chặn (block) cho tới khi hoạt động này hoàn thành. Các hoạt động IO khác có thể tiến hành đồng thời với hoạt động đọc hay không phụ thuộc vào loại Channel.

4. Example 1

Ví dụ: Sử dụng ReadableByteChannel để đọc dữ liệu từ một InputStream. Cụ thể là từ một ByteArrayInputStream.
Dưới đây là hình ảnh các bytes của chuỗi "JP日本-八洲" trong định dạng UTF-8 (UTF-8 sử dụng 1, 2, 3 hoặc 4 bytes để lưu trữ một ký tự).
ReadableByteChannel_ex1.java
package org.o7planning.readablebytechannel.ex;

import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

public class ReadableByteChannel_ex1 {

    public static void main(String[] args) throws IOException {
        InputStream inputStream = null;
        ReadableByteChannel channel = null;
        try {
            byte[] byteData = "JP日本-八洲".getBytes("UTF-8");
            inputStream = new ByteArrayInputStream(byteData);
            
            // Create ReadableByteChannel to read a InputStream.
            channel = Channels.newChannel(inputStream);
            ByteBuffer buffer = ByteBuffer.allocate(10);

            int bytesRead = -1;
            while ((bytesRead = channel.read(buffer)) > -1) {
                System.out.println(" --- bytesRead : " + bytesRead + " ---- ");
                // Set limit = current position; position = 0;
                buffer.flip();

                while (buffer.hasRemaining()) {
                    byte b = buffer.get(); // [-128,127]
                    int charCode = Byte.toUnsignedInt(b); // [0,255] Java 8+
                    System.out.println((char) charCode + " --> " + charCode);
                }
                // Set position = 0; limit = capacity.
                buffer.clear();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeQuietly(inputStream);
            closeQuietly(channel);
        }
    }

    private static void closeQuietly(Closeable closeable) {
        try {
            closeable.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Output:
--- bytesRead : 10 ----
J --> 74
P --> 80
æ --> 230
— --> 151
¥ --> 165
æ --> 230
œ --> 156
¬ --> 172
- --> 45
å --> 229
 --- bytesRead : 5 ----
… --> 133
« --> 171
æ --> 230
´ --> 180
² --> 178
Cả InputStreamChannel đều thi hành (implements) hoặc mở rộng từ interface Closeable, vì vậy nó có khả năng tự động đóng nếu bạn sử dụng cú pháp "Closeable-try-catch". Và chúng ta viết lại ví dụ trên một cách ngắn gọn hơn.
ReadableByteChannel_ex1b.java
package org.o7planning.readablebytechannel.ex;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

public class ReadableByteChannel_ex1b {

    public static void main(String[] args) throws IOException {
        byte[] byteData = "JP日本-八洲".getBytes("UTF-8");

        // Closeable-try-catch Syntax:
        try (InputStream inputStream = new ByteArrayInputStream(byteData);
                // Create ReadableByteChannel to read a InputStream.
                ReadableByteChannel channel = Channels.newChannel(inputStream);) { // try
            ByteBuffer buffer = ByteBuffer.allocate(10);

            int bytesRead = -1;
            while ((bytesRead = channel.read(buffer)) > -1) {
                System.out.println(" --- bytesRead : " + bytesRead + " ---- ");
                // Set limit = current position; position = 0;
                buffer.flip();

                while (buffer.hasRemaining()) {
                    byte b = buffer.get(); // [-128,127]
                    int charCode = Byte.toUnsignedInt(b); // [0,255] Java 8+
                    System.out.println((char) charCode + " --> " + charCode);
                }
                // Set position = 0; limit = capacity.
                buffer.clear();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5. Example 2

Ví dụ: Đọc một file dữ liệu trên mạng.
ReadableByteChannel_ex2.java
String urlString = "https://s3.o7planning.com/txt/utf8-file-without-bom.txt";
URL url = new URL(urlString);

try (InputStream inputStream = url.openStream();
        // Create ReadableByteChannel to read a InputStream.
        ReadableByteChannel channel = Channels.newChannel(inputStream);) { // try
    
    ByteBuffer buffer = ByteBuffer.allocate(10);
    int bytesRead = -1;
    while ((bytesRead = channel.read(buffer)) > -1) {
        System.out.println(" --- bytesRead : " + bytesRead + " ---- ");
        // Set limit = current position; position = 0;
        buffer.flip();

        while (buffer.hasRemaining()) {
            byte b = buffer.get(); // [-128,127]
            int charCode = Byte.toUnsignedInt(b); // [0,255] Java 8+
            System.out.println((char) charCode + " --> " + charCode);
        }
        // Set position = 0; limit = capacity.
        buffer.clear();
    }
} catch (Exception e) {
    e.printStackTrace();
}
Output:
--- bytesRead : 10 ----
J --> 74
P --> 80
æ --> 230
— --> 151
¥ --> 165
æ --> 230
œ --> 156
¬ --> 172
- --> 45
å --> 229
 --- bytesRead : 5 ----
… --> 133
« --> 171
æ --> 230
´ --> 180
² --> 178