NIO2非同期ファイルチャンネルの手引き
1概要
この記事では、Java 7の新しいI/O(NIO2)の主要な追加APIの1つである非同期ファイル・チャネルAPIについて説明します。
一般的に非同期チャネルAPIに慣れていない場合は、このサイトに紹介記事があります。リンクを読んで先に進むことができます。
NIO.2のリンクについてもっと読むことができます:/java-nio-2-file-api[ファイル操作]と
パス操作
– これらを理解することはこの記事をずっと簡単にするでしょうフォローする。
私たちのプロジェクトでNIO2非同期ファイルチャンネルを使うためには、
java.nio.channels
パッケージがすべての必要なクラスを束ねているのでインポートする必要があります。
import java.nio.channels.** ;
** 2
AsynchronousFileChannel
**
このセクションでは、ファイルに対して非同期操作を実行することを可能にするメインクラスである
AsynchronousFileChannel
クラスの使用方法を探ります。そのインスタンスを作成するために、静的
open
メソッドを呼び出します。
Path filePath = Paths.get("/path/to/file");
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
filePath, READ, WRITE, CREATE, DELETE__ON__CLOSE);
すべてのenum値はSta
__ndardOpenOption
__から来ます。
open APIの最初のパラメータはファイルの場所を表す
Path
オブジェクトです。 NIO2でのパス操作の詳細については、リンク:/java-nio-2-path[このリンク]をたどってください。他のパラメータは、返されたファイルチャンネルに利用可能であるべきオプションを指定するセットを構成します。
作成した非同期ファイルチャネルを使用して、ファイルに対して既知の操作をすべて実行できます。操作のサブセットのみを実行するには、それらのオプションに対してのみオプションを指定します。例えば、読むだけなら:
Path filePath = Paths.get("/path/to/file");
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
filePath, StandardOpenOption.READ);
3ファイルからの読み取り
NIO2のすべての非同期操作と同様に、ファイルの内容の読み取りは2つの方法で実行できます。
Future
を使用して
CompletionHandler
を使用します。いずれの場合も、返されたチャネルの
read
APIを使用します。
mavenのテストリソースフォルダ内、またはmavenを使用していない場合はソースディレクトリ内に、先頭に
baeldung.com
というテキストのみを含む
file.txt
という名前のファイルを作成します。このコンテンツの読み方を説明します。
3.1. 今後の取り組み
まず、
Future
クラスを使ってファイルを非同期的に読み込む方法を見ていきます。
@Test
public void givenFilePath__whenReadsContentWithFuture__thenCorrect() {
Path path = Paths.get(
URI.create(
this.getClass().getResource("/file.txt").toString()));
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
path, StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> operation = fileChannel.read(buffer, 0);
//run other code as operation continues in background
operation.get();
String fileContent = new String(buffer.array()).trim();
buffer.clear();
assertEquals(fileContent, "baeldung.com");
}
上記のコードでは、ファイルチャンネルを作成した後、
read
APIを使用します。これは
ByteBuffer
を使用して、チャンネルから読み取ったコンテンツを最初のパラメーターとして格納します。
2番目のパラメータは、読み取りを開始するファイル内の位置を示すlongです。
ファイルが読み込まれたかどうかにかかわらず、メソッドはすぐに戻ります。
次に、操作がバックグラウンドで続行されるので、他のコードを実行できます。他のコードの実行が終了したら、
get()
APIを呼び出して、他のコードの実行時に操作がすでに完了している場合はすぐに戻ります。それ以外の場合は、操作が完了するまでブロックします。
私たちの主張は確かにファイルの内容が読まれたことを証明します。
read
API呼び出しのpositionパラメーターをゼロから別のものに変更した場合は、その効果も見られます。たとえば、文字列
baeldung.com
の7文字目は
g
です。そのため、positionパラメータを7に変更すると、バッファに文字列
g.com
が含まれるようになります。
3.2.
CompletionHandler
アプローチ
次に、
CompletionHandler
インスタンスを使用してファイルの内容を読み取る方法を説明します。
@Test
public void
givenPath__whenReadsContentWithCompletionHandler__thenCorrect() {
Path path = Paths.get(
URI.create( this.getClass().getResource("/file.txt").toString()));
AsynchronousFileChannel fileChannel
= AsynchronousFileChannel.open(path, StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
fileChannel.read(
buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
//result is number of bytes read
//attachment is the buffer containing content
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
});
}
上記のコードでは、
read
APIの2番目のバリアントを使用しています。それはまだ
ByteBuffer
と
read
操作の開始位置をそれぞれ1番目と2番目のパラメータとして取ります。 3番目のパラメータは
CompletionHandler
インスタンスです。
完了ハンドラの最初のジェネリック型は操作の戻り型、この場合は読み込まれたバイト数を表す整数です。
2番目は添付ファイルの種類です。
read
が完了したときに
completed
コールバックAPI内のファイルの内容を使用できるように、バッファーをアタッチすることを選択しました。
意味的に言えば、これは実際には有効な単体テストではありません。これは、
completed
コールバックメソッド内でアサーションを実行できないためです。ただし、これは一貫性を保つため、またコードをできるだけ
__copy-paste-run –
__できるようにしたいためです。
4ファイルへの書き込み
Java NIO 2では、ファイルに対して書き込み操作を実行することもできます。他の操作と同じように、2つの方法でファイルに書き込むことができます。
Future
を使用して
CompletionHandler
を使用します。いずれの場合も、返されたチャネルの
write
APIを使用します。
ファイルに書き込むための
AsynchronousFileChannel
の作成は、次のようにして実行できます。
AsynchronousFileChannel fileChannel
= AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
4.1. 特別な考慮事項
open
APIに渡されたオプションに注目してください。
path
で表されるファイルがまだ存在しない場合に作成する場合は、別のオプション
StandardOpenOption.CREATE
を追加することもできます。もう1つの一般的なオプションは
StandardOpenOption.APPEND
です。これはファイル内の既存のコンテンツを上書きしません。
テスト目的でファイルチャネルを作成するために次の行を使用します。
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
path, WRITE, CREATE, DELETE__ON__CLOSE);
このようにして、任意のパスを指定し、ファイルが作成されることを確認します。テスト終了後、作成されたファイルは削除されます。
テスト終了後に作成されたファイルが削除されないようにするために、最後のオプションを削除できます。
アサーションを実行するには、アサーションを書いた後、可能な限りファイルの内容を読む必要があります。冗長性を避けるために、読み取りのロジックを別の方法で隠しましょう。
public static String readContent(Path file) {
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
file, StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> operation = fileChannel.read(buffer, 0);
//run other code as operation continues in background
operation.get();
String fileContent = new String(buffer.array()).trim();
buffer.clear();
return fileContent;
}
4.2. __未来のアプローチ
Future
クラスを使用してファイルに非同期的に書き込むには、次のようにします。
@Test
public void
givenPathAndContent__whenWritesToFileWithFuture__thenCorrect() {
String fileName = UUID.randomUUID().toString();
Path path = Paths.get(fileName);
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
path, WRITE, CREATE, DELETE__ON__CLOSE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("hello world".getBytes());
buffer.flip();
Future<Integer> operation = fileChannel.write(buffer, 0);
buffer.clear();
//run other code as operation continues in background
operation.get();
String content = readContent(path);
assertEquals("hello world", content);
}
上記のコードで何が起こっているのか調べてみましょう。ランダムなファイル名を作成し、それを使用して
Path
オブジェクトを取得します。このパスを使用して、前述のオプションで非同期ファイルチャネルを開きます。
次に、書き込みたいコンテンツをファイルにバッファに入れて
write
を実行します。私たちはファイルの内容を読むために私たちのヘルパーメソッドを使い、それが期待通りのものであることを確かに確認します。
4.3.
CompletionHandler
アプローチ
補完ハンドラを使用して、操作がwhileループで完了するのを待つ必要がないようにすることもできます。
@Test
public void
givenPathAndContent__whenWritesToFileWithHandler__thenCorrect() {
String fileName = UUID.randomUUID().toString();
Path path = Paths.get(fileName);
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
path, WRITE, CREATE, DELETE__ON__CLOSE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("hello world".getBytes());
buffer.flip();
fileChannel.write(
buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
//result is number of bytes written
//attachment is the buffer
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
});
}
今回write APIを呼び出すときに唯一新しいことは、
CompletionHandler
型の匿名内部クラスを渡す3番目のパラメータです。
操作が完了すると、クラスはそれが完了すべきメソッドを呼び出し、その中で何が起こるべきかを定義できます。
5結論
この記事では、Java NIO2のAsynchronous File Channel APIの最も重要な機能のいくつかを調べました。
この記事のすべてのコードスニペットと完全なソースコードを入手するには、https://github.com/eugenp/tutorials/tree/master/core-java-io[Githubプロジェクト]にアクセスしてください。