Статьи

Чтение / запись в / из файлов с использованием FileChannel и ByteBuffer в Java

В прошлом я говорил о RandomAccessFile и о том, как его можно использовать для ускорения ввода-вывода в Java, а в этом руководстве по Java NIO мы увидим, как использовать данные для чтения / записи при использовании FileChannel и ByteBuffer .

Канал предоставляет альтернативный способ чтения данных из файла, он обеспечивает лучшую производительность, чем InputStream или OutputStream. Его также можно открыть в режиме блокировки и неблокирования. Хотя FileChannles являются каналами чтения / записи, и они всегда блокируют , их нельзя перевести в неблокирующий режим. Класс RandomAccessFile обрабатывает файл как массив байтов.

Вы можете записать свои данные в любую позицию массива, и вы можете читать из любой позиции. Для этого он использует указатель, который содержит текущую позицию и предоставляет несколько методов, таких как seek() для перемещения этого указателя. Как только вы окажетесь в правильном положении, вы можете получить FileChannel из RandomAccessFile и начать чтение данных из файла. Кстати, JDK 7 также представил NIO 2, что делает работу с файлами и каталогами еще проще. Прочитайте про Java 7 NIO.2 Ангела Леонарда, чтобы узнать больше о.

Как читать / записывать файлы, используя FileChannel и ByteBuffer

Прежде чем приступить к написанию кода, давайте пересмотрим базовую концепцию Channel и Buffer в Java NIO. Одним словом, буферы работают с каналом. Каналы — это труба, по которой передаются данные, а буферы являются источником и целью этой передачи данных. В случае записи данные, которые вы хотите записать, помещаются в буфер, который передается в канал, а затем канал считывает эти данные из буфера и записывает в файл.

Точно так же в случае чтения канал помещает данные в буфер, который вы предоставляете из файла , сети или любого другого источника. Поскольку один и тот же буфер используется для чтения и записи, т.е. вы записываете данные в буфер, но канал считывает их для записи в файл, вы должны вызвать метод flip () после завершения записи в буфер. Метод flip () изменяет указатели и позволяет читать данные из буфера. В Java существует три типа буфера: прямой, непрямой и сопоставленный буфер . В этом примере мы будем использовать прямой байтовый буфер.

Шаги для чтения / записи данных с использованием FileChannel и Buffer

Вот пошаговое руководство по началу чтения данных из файла с использованием RandomAccessFile , FileChannel и ByteBuffer :

  1. Откройте файл, который вы хотите прочитать / записать, используя RandomAccessFile в режиме чтения / записи.
  2. Вызовите метод getChannel() для RandomAccessFile, чтобы получить FileChannel. Положение возвращаемого канала всегда будет равно смещению указателя файла этого объекта, как возвращено методом getFilePointer() .
  3. Создайте ByteBuffer с помощью ByteBuffer.allocate() .
  4. Сохраните данные в ByteBuffer, используя различные методы putInt() , например, putInt() , putLong() .
  5. Переверните буфер, чтобы канал мог читать данные из буфера и записывать в файл. Метод flip () изменяет указатели и позволяет читать данные из буфера.
  6. Вызовите метод write () FileChannel.
  7. Закройте FileChannel
  8. Закройте файл RandomAccessFile.

Еще один важный момент, на который следует обратить внимание, это то, что вы можете использовать один и тот же буфер для чтения и записи, но его нужно перевернуть Теперь давайте рассмотрим пример Java-программы для чтения / записи данных из файлов, используя FileChannel и ByteBuffer в Java. После Memory Mapped File это второй самый быстрый способ чтения и записи из файла в Java.

ByteBuffer FileChannel Java

Java программа для чтения / записи из файла с использованием FileChannel и ByteBuffer

Вот пример программы, чтобы продемонстрировать, как вы можете читать и записывать данные из файла (может быть двоичного или текстового файла) с использованием классов FileChannel и ByteBuffer. Я также использовал абстракцию для создания интерфейса под названием Persistable, который предоставляет два метода persist() и recover() . Любой объект, который реализует этот интерфейс, может быть сохранен и загружен, но как вы сохраняете и загружаете их, оставлено на усмотрение разработчика, т.е. вы можете использовать Chanel и Buffer, как мы это делали, или вы можете использовать старый подход для чтения / записи файла в Java ,

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
 
/**
 * Java Program to read and write on RandomAccessFile in Java
 * using FileChannle and ByteBuffer.
 *
 * @author Javin
 */
public class FileChannelDemo {
 
    public static void main(String args[]) {
 
        Tablet ipad = new Tablet("Apple", true, 1000);
        System.out.println("Writing into RandomAcessFile : " + ipad);
        write("tablet.store", ipad);
 
        Tablet fromStore = new Tablet();
        read("tablet.store", fromStore);
        System.out.println("Object read from RandomAcessFile : " + fromStore);
 
    }
 
    /*
     * Method to write data into File using FileChannel and ByteBuffeer
     */
    public static void write(String filename, Persistable object) {
        try {
            // Creating RandomAccessFile for writing
            RandomAccessFile store = new RandomAccessFile("tablet", "rw");
 
            // getting FileChannel from file
            FileChannel channel = store.getChannel();
 
            // creating and initializing ByteBuffer for reading/writing data
            ByteBuffer buffer = ByteBuffer.allocate(2048);
 
            // an instance of Persistable writing into ByteBuffer
            object.persist(buffer);
 
            // flip the buffer for writing into file
            buffer.flip();
            int numOfBytesWritten = channel.write(buffer); // writing into File
            System.out.println("number of bytes written : " + numOfBytesWritten);
            channel.close(); // closing file channel
            store.close(); // closing RandomAccess file
 
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
 
    /*
     * Method to read data from File using FileChannel and ByteBuffeer
     */
    public static void read(String filename, Persistable object) {
        try {
            // Opening RandomAccessFile for reading data
            RandomAccessFile store = new RandomAccessFile("tablet", "rw");
 
            // getting file channel
            FileChannel channel = store.getChannel();
 
            // preparing buffer to read data from file
            ByteBuffer buffer = ByteBuffer.allocate(1024);
 
            // reading data from file channel into buffer
            int numOfBytesRead = channel.read(buffer);
            System.out.println("number of bytes read : " + numOfBytesRead);
 
            // You need to filp the byte buffer before reading
            buffer.flip();
 
            // Recovering object
            object.recover(buffer);
 
            channel.close();
            store.close();
 
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Наш интерфейс для абстрактного механизма чтения и записи. Это также фактическое использование интерфейса для обеспечения абстракции, отделяющей то, что нужно сделать, от того, как это сделать. Подобно этому интерфейсу, просто скажите «сохранитесь и восстановитесь», а не как вы это делаете.

1
2
3
4
5
6
interface Persistable {
 
    public void persist(ByteBuffer buffer);
 
    public void recover(ByteBuffer buffer);
}

Конкретный класс для реализации Persistable, чтобы сделать их читаемыми и записываемыми:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class Tablet implements Persistable {
 
    private String brand;
    private boolean isCellular;
    private long cost; // in US Dollars
 
    public Tablet() {
        brand = "";
    }
 
    public Tablet(String brand, boolean isCellular, long cost) {
        this.brand = brand;
        this.isCellular = isCellular;
        this.cost = cost;
    }
 
    public final String getBrand() {
        return brand;
    }
 
    public final boolean isCellular() {
        return isCellular;
    }
 
    public final long getCost() {
        return cost;
    }
 
    public final void setBrand(String brand) {
        this.brand = brand;
    }
 
    public final void setCellular(boolean isCellular) {
        this.isCellular = isCellular;
    }
 
    public final void setCost(long cost) {
        this.cost = cost;
    }
 
    @Override
    public void persist(ByteBuffer buffer) {
        byte[] strBytes = brand.getBytes();
        buffer.putInt(strBytes.length);
        buffer.put(strBytes, 0, strBytes.length);
        buffer.put(isCellular == true ? (byte) 1 : (byte) 0);
        buffer.putLong(cost);
    }
 
    @Override
    public void recover(ByteBuffer buffer) {
        int size = buffer.getInt();
        byte[] rawBytes = new byte[size];
        buffer.get(rawBytes, 0, size);
        this.brand = new String(rawBytes);
        this.isCellular = buffer.get() == 1 ? true : false;
        this.cost = buffer.getLong();
    }
 
    @Override
    public String toString() {
        return "Tablet [brand=" + brand + ", isCellular=" + isCellular + ", cost=" + cost + "]";
    }
 
}
 
  
  
Output:
Writing into RandomAcessFile : Tablet [brand=Apple, isCellular=true, cost=1000]
number of bytes written : 18
number of bytes read : 1024
Object read from RandomAcessFile : Tablet [brand=Apple, isCellular=true, cost=1000]

предосторожность

Не забудьте перевернуть байтовый буфер после записи в него содержимого объекта, потому что файловый канал должен прочитать его, чтобы записать данные в RandomAccessFile. Если вы забудете вызвать метод flip () перед вызовом FileChannel.write (), то в итоге вы ничего не напишите в файл.

Аналогично, после считывания данных из файла в буфер снова переверните его, чтобы вы могли прочитать данные из буфера в популярное содержимое объекта. Многие Java-программисты делают эту ошибку, не переворачивая после записи и заканчивая часами отладки, потому что либо ничего не записывается в файл, либо ничего не может прочитать из файла.

Это все о том, как читать / записывать файл, используя FileChannel и ByteBuffer в Java . В этом демоне я показал вам, как читать и писать файл RandomAccessFile, используя FileChannel и ByteBuffer, но вы можете применить ту же технику для чтения любого другого текстового или двоичного файла из программы Java.