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))。还是很容易理解的吧!

posted @ 2018-04-16 15:18  kakaisgood  阅读(126)  评论(0编辑  收藏  举报