Java NIO系列七之 AsynchronousFileChannel非同步檔案通道
Java7中新增了AsynchronousFileChannel作為nio的一部分。AsynchronousFileChannel使得資料可以進行非同步讀寫。下面將介紹一下AsynchronousFileChannel的使用。
建立AsynchronousFileChannel(Creating an AsynchronousFileChannel)
AsynchronousFileChannel的建立可以通過open()靜態方法:
Path path = Paths.get("data/test.xml"); AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
open()的第一個引數是一個Path實體,指向我們需要操作的檔案。 第二個引數是操作型別。上述示例中我們用的是StandardOpenOption.READ,表示以讀的形式操作檔案。
讀取資料(Reading Data)
讀取AsynchronousFileChannel的資料有兩種方式。每種方法都會呼叫AsynchronousFileChannel的一個read()介面。下面分別看一下這兩種寫法。
通過Future讀取資料(Reading Data Via a Future)
第一種方式是呼叫返回值為Future的read()方法:
Future<Integer> operation = fileChannel.read(buffer, 0);
這種方式中,read()接受一個ByteBuffer座位第一個引數,資料會被讀取到ByteBuffer中。第二個引數是開始讀取資料的位置。
read()方法會立刻返回,即使讀操作沒有完成。我們可以通過isDone()方法檢查操作是否完成。
下面是一個略長的示例:
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ); ByteBuffer buffer = ByteBuffer.allocate(1024); long position = 0; Future<Integer> operation = fileChannel.read(buffer, position); while(!operation.isDone()); buffer.flip(); byte[] data = new byte[buffer.limit()]; buffer.get(data); System.out.println(new String(data)); buffer.clear();
在這個例子中我們建立了一個AsynchronousFileChannel,然後建立一個ByteBuffer作為引數傳給read。接著我們建立了一個迴圈來檢查是否讀取完畢isDone()。這裡的迴圈操作比較低效,它的意思是我們需要等待讀取動作完成。
一旦讀取完成後,我們就可以把資料寫入ByteBuffer,然後輸出。
通過CompletionHandler讀取資料(Reading Data Via a CompletionHandler)
另一種方式是呼叫接收CompletionHandler作為引數的read()方法。下面是具體的使用:
fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("result = " + result);
attachment.flip();
byte[] data = new byte[attachment.limit()];
attachment.get(data);
System.out.println(new String(data));
attachment.clear();
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
});
這裡,一旦讀取完成,將會觸發CompletionHandler的completed()方法,並傳入一個Integer和ByteBuffer。前面的整形表示的是讀取到的位元組數大小。第二個ByteBuffer也可以換成其他合適的物件方便資料寫入。 如果讀取操作失敗了,那麼會觸發failed()方法。
寫資料(Writing Data)
和讀資料類似某些資料也有兩種方式,調動不同的的write()方法,下面分別看介紹這兩種方法。
通過Future寫資料(Writing Data Via a Future)
通過AsynchronousFileChannel我們可以一步寫資料
Path path = Paths.get("data/test-write.txt");
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
buffer.put("test data".getBytes());
buffer.flip();
Future<Integer> operation = fileChannel.write(buffer, position);
buffer.clear();
while(!operation.isDone());
System.out.println("Write done");
首先把檔案已寫方式開啟,接著建立一個ByteBuffer座位寫入資料的目的地。再把資料進入ByteBuffer。最後檢查一下是否寫入完成。 需要注意的是,這裡的檔案必須是已經存在的,否者在嘗試write資料是會丟擲一個java.nio.file.NoSuchFileException.
檢查一個檔案是否存在可以通過下面的方法:
if(!Files.exists(path)){
Files.createFile(path);
}
通過CompletionHandler寫資料(Writing Data Via a CompletionHandler)
我們也可以通過CompletionHandler來寫資料:
Path path = Paths.get("data/test-write.txt");
if(!Files.exists(path)){
Files.createFile(path);
}
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
buffer.put("test data".getBytes());
buffer.flip();
fileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("bytes written: " + result);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("Write failed");
exc.printStackTrace();
}
});
同樣當資料吸入完成後completed()會被呼叫,如果失敗了那麼failed()會被呼叫。