java NIO FileChannel
http://www.cnblogs.com/puyangsky/p/5840873.html
2.2.2 文件通道
2.2.2.1 打开
FileChannel只能通过工厂方法来实例化,那就是调用RandomAccessFile、FileInputStream和FileOutputStream的getChannel()方法。如:
RandomAccessFile file = new RandomAccessFile("a.txt", "r");
FileChannel fc = file.getChannel();
2.2.2.2 使用
先看看FileChannel提供的方法句柄:
public abstract int read(ByteBuffer dst) throws IOException;//把通道中数据传到目的缓冲区中,dst是destination的缩写
public abstract int write(ByteBuffer src) throws IOException;//把源缓冲区中的内容写到指定的通道中去
从句柄可以看出FileChannel是既可以读又可以写的,是全双工的。下面这个例子用来展示FileChannel是如何进行读和写的。
public class FileChannelTest {
public static void readFile(String path) throws IOException {
FileChannel fc = new FileInputStream(path).getChannel();
ByteBuffer buffer = ByteBuffer.allocate(128);
StringBuilder sb = new StringBuilder();
while ((fc.read(buffer)) >= 0) {
//翻转指针
buffer.flip();
//remaining = limit - position
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String string = new String(bytes, "UTF-8");
sb.append(string);
//清空buffer
buffer.clear();
}
System.out.println(sb.toString());
}
public static void writeFile(String path, String string) throws IOException {
FileChannel fc = new FileOutputStream(path).getChannel();
ByteBuffer buffer = ByteBuffer.allocate(10);
int current = 0;
int len = string.getBytes().length;
while (current < len) {
for (int i=0;i<10;i++) {
if (current+i>=len) break;
buffer.put(string.getBytes()[current+i]);
}
current += buffer.position();
buffer.flip();
fc.write(buffer);
buffer.clear();
}
}
public static void main(String[] args) throws IOException {
String in = "D:/in.txt";
String out = "D:/out.txt";
readFile(in);
writeFile(out, "hello world");
readFile(out);
}
}
分析一下上面这段代码,在readFile()函数中,通过FileInputStream.getChannel()得到FileChannel对象,并创建ByteBuffer对象,接着利用FileChannel的read方法填充buffer,得到填充完的buffer之后我们将buffer的当前指针翻转一下接着利用buffer的get方法把数据放到byte数组中,接着就可以读取数据了。
读取文件的整个过程相比原生的I/O方法还是略显麻烦,但是我们如果把数据看成一堆煤矿,把ByteBuffer看成装煤的矿车,而FileChannel看成是运煤的矿道,那么上面的过程就演变成了:先打通一条矿道,然后把煤矿装在小车里运出来。形象的记忆更利于理解这个过程。
而writeFile()函数也是类似,为了更好的理解Buffer的属性,我特意将buffer的大小设置为10,为要写入的字符串长度为11个字节。首先还是通过FileOutputStream.getChannel()方法得到FileChannel对象,并创建一个10字节大小的缓冲区,接着定义一个整型变量current指向要写入的字符串的当前下标,每次向buffer中put10个字节,并更新current,通过buffer.position()方法可以得到buffer被填充之后指针的位置,也就是缓冲区里的字节个数,然后翻转指针,最后通过FileChannel.write(buffer)方法将buffer写入到文件中。
同样考虑一下形象化的过程:我们首先把煤矿装入小车(buffer.put()),并打开一条通往矿山的矿道(FileOutputStream.getChannel()),接着把煤矿运输进去(FileChannel.write(buffer))。还是很容易理解的吧!