チャネルを使用するには、とにもかくにも、チャネルが手元(変数)になければなりません。
ファイルチャネルは、とりあえずFileInputStreamやFileOutputStreamを作成してから、 チャネルだけを取り出します。
final String inputFile = "./sampleinput.txt"; FileInputStream in = new FileInputStream(inputFile); FileChannel fileInputChannel = in.getChannel(); final String outputFile = "./sampleoutput.txt"; FileOutputStream out = new FileOutputStream(outputFile); FileChannel fileOutputChannel = out.getChannel();
ソケットチャネルは、ソケットチャネルのクラスから作成します。
SocketChannel clientChannel = SocketChannel.open(); ServerSocketChannel serverChannel = ServerSocketChannel.open(); DatagramChannel datagramChannel = DatagramChannel.open();
取得したチャネルは、バッファと組み合わせることで実際に書き込み/読み出しを行う ことができます。
チャネルがストリームからデータを読み込むと、チャネルはバッファにデータを書き出し ます。
+------------+ +----------+ +----------+ +------------+ | | 読み込み | | 格納 | | 取得 | | | ストリーム |---------->| チャネル |--------->| バッファ |--------->| プログラム | | | | | put | | get | | +------------+ +----------+ +----------+ +------------+
逆に チャネルがストリームへデータを書き出すとき、チャネルはバッファからデータを読み込み ます。
+------------+ +----------+ +----------+ +------------+ | | 書き出し | | 取得 | | 格納 | | | ストリーム |<----------| チャネル |<---------| バッファ |<---------| プログラム | | | | | get | | put | | +------------+ +----------+ +----------+ +------------+
チャネルとバッファを組み合わせたストリームの読み込みのサンプルを示します。
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ChannelOutputSample {
public static void main(String[] args)
throws FileNotFoundException, IOException {
// データ
byte[] data = "abcdefg hijklmn opqrstu vwxyz".getBytes();
// アウトプット用ファイルのオープン
final String filename = "sample.txt";
FileOutputStream outStream = new FileOutputStream(filename);
// アウトプット用チャネルの取得
FileChannel channel = outStream.getChannel();
// アウトプット用バッファの作成
ByteBuffer buff = ByteBuffer.allocateDirect(256);
// バッファにデータを格納
buff.put(data);
// 格納したデータをchannelがgetできるようにする。
buff.flip();
// バッファからすべてのデータをgetし、
//それをチャネルにデータを書き出す
channel.write(buff);
// チャネルのクローズ
channel.close();
}
}
これを実行すると、カレントディレクトリに、"sample.txt"というファイルが作成されます。
次に、先ほど書き出したファイルを読み込んで、表示するプログラムを作成してみます。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ChannelInputSample {
public static void main(String[] args)
throws FileNotFoundException, IOException {
// インプット用ファイルのオープン
final String filename = "sample.txt";
FileInputStream inStream = new FileInputStream(filename);
// インプット用チャネルの取得
FileChannel channel = inStream.getChannel();
// インプット用バッファの作成
ByteBuffer buff = ByteBuffer.allocateDirect(256);
// データの取得
channel.read(buff);
// チャネルをクローズ
channel.close();
// データをバッファからgetする準備
buff.flip();
// データの取得と表示
byte[] data = new byte[buff.limit()];
buff.get(data);
System.out.println(new String(data));
}
}
abcdefg hijklmn opqrstu vwxyz
実行すると、先ほどファイルに書き込んだデータが表示されます。 ちゃんと動いてますね。
大きなデータをチャネルとバッファで操作するときのサンプルプログラムを記載します。 大きなデータをチャネルで扱うときは、一度の処理でバッファにデータが入りきらないため、 何度にも分けてバッファへの格納とバッファからの取り出し(ファイルへの書き出し)を繰り返します。 この際に、バッファに格納できる要素の数を知るには、ByteBuffer#remaining()を使用すると便利です。
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
public class LargeDataOutput {
private static final int DATASIZE = 10 * 1024 * 1024;// 10MByte
private static final int BUFFSIZE = 1024;
private static final File file = new File("sampleFile.txt");
public static void main(String[] args)
throws FileNotFoundException, IOException {
// 書き出すデータの作成(10MByteすべて'a'のデータ)
byte[] data = new byte[DATASIZE];
Arrays.fill(data, (byte)'a');
// チャネルの取得
FileChannel channel = new FileOutputStream(file).getChannel();
// バッファの取得
ByteBuffer buff = ByteBuffer.allocateDirect(BUFFSIZE);
// ファイルへ書き込み
int idxBegin = 0;
int length = 0;
// データをすべてバッファに格納するまでループ
while (idxBegin < DATASIZE) {
idxBegin += length; // バッファに格納するデータの開始位置
length = buff.remaining(); // バッファに格納するデータの長さ
// idxBegin + lengthがdata配列のアクセス可能範囲を超えないように調整
if (idxBegin + length >= DATASIZE) {
length = DATASIZE - idxBegin;
}
buff.put(data, idxBegin, length);// バッファにデータを格納する
buff.flip();
channel.write(buff); // チャネルがデータをバッファから取得
buff.compact(); // バッファへのデータの格納に備える
};
// バッファのデータがすべてファイルに書き出されるまでループ
while (buff.hasRemaining()) {
channel.write(buff);
}
channel.close();
}
}
実行すると、カレントディレクトリにsampleFile.txtというファイルが10MByteのサイズで作成されていると思います。 テキストエディタで開いてみると、すべてaの文字で埋め尽くされていたら成功です。 (サイズが大きすぎて、OSが不安定になる場合があるので注意してください)