【NIO】5. FileChannel的基本用法

FileChannel的基本用法

  • [1. 获取FileChannel对象](#1. 获取FileChannel对象)
  • [2. 从FileChannel中读取数据(channel.read(buffer)))](#2. 从FileChannel中读取数据(channel.read(buffer))))
  • [3. 向FileChannel中写入数据(channel.write(buffer))](#3. 向FileChannel中写入数据(channel.write(buffer)))
  • [4. 关闭FileChannel](#4. 关闭FileChannel)
  • [5. transferTo](#5. transferTo)
    • [5.1 文件不超过2g,可以一次传输完成](#5.1 文件不超过2g,可以一次传输完成)
    • [5.2 文件超过2g,则多次传输](#5.2 文件超过2g,则多次传输)

FileChannel只能工作在【阻塞模式】下;(不能配合selector工作在非阻塞模式)

1. 获取FileChannel对象

FileChannel对象不能直接创建,需通过输入输出流来获取FileChannel对象

  • FileInputStream的getChannel()方法;
  • FileOutputStream的getChannel()方法;
  • RandomAccessFile的getChannel()方法;

channel虽然是数据读写的双向通道,但是

  • 通过FileInputStream获取的FileChannel只能读
  • 通过FileOutputStream获取的FileChannel只能写
  • 通过RandomAccessFile获取的FileChannel对象,根据构造RandomAccessFile对象时的读写模式决定;

示例:

java 复制代码
public static void main(String[] args) {

        try(FileInputStream fileInputStream = new FileInputStream("text.txt")) {
            FileChannel channel = fileInputStream.getChannel();

            // 从内存中分配一个buffer,大小为10个字节
            ByteBuffer byteBuffer = ByteBuffer.allocate(10);

            // ByteBuffer中写入数据
            byteBuffer.put("abc".getBytes(StandardCharsets.UTF_8));

            // 试图从Buffer中读取数据,写入channel
            // 由于当前channel对象是通过FileInputStream获取的,只能读,不能写
            int write = channel.write(byteBuffer);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

输出结果:

通过FileInputStream获取的FileChannel只能读,不能写

text 复制代码
"C:\Program Fis\Java\jdk-1.8\bin\java.exe" ...
Exception in thread "main" java.nio.channels.NonWritableChannelException
	at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:201)
	at com.weimin.part01_buffer.Buffer01_HelloWorld1.main(Buffer01_HelloWorld1.java:29)

Process finished with exit code 1

2. 从FileChannel中读取数据(channel.read(buffer)))

java 复制代码
public static void main(String[] args) {
        try(FileInputStream fileInputStream = new FileInputStream("text.txt")) {
            // 获取文件channel
            FileChannel channel = fileInputStream.getChannel();

            // 从内存中分配一个buffer,大小为10个字节
            ByteBuffer byteBuffer = ByteBuffer.allocate(10);

            int read;

            // read方法的返回值代表读取到的字节数,当返回值为-1时,代表文件中的所有数据读取完成
            // 使用while一直读取,直到全部读取完
            while ((read = channel.read(byteBuffer)) != -1) {
                System.out.println("读到的字节数:" + read);
                // flip方法,切换到【读模式】;flip在这里的意思应该是【浏览 / 快速翻阅】
                byteBuffer.flip();
                while (byteBuffer.hasRemaining()) {
                    byte b = byteBuffer.get();
                    System.out.println((char) b);
                }
                // buffer中的数据读取完成后,清空buffer的数据
                byteBuffer.clear();
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

3. 向FileChannel中写入数据(channel.write(buffer))

该例中,在从buffer中读取数据之前,没有调用flip方法,因为通过StandardCharsets.UTF_8.encode方法获得的buffer对象,已经在内部调用过flip方法了。

在while语句中调用fileChannel.write(buffer),是因为write方法并不能保证一次将buffer中的内容全部写入channel

java 复制代码
public static void main(String[] args) {
        try (FileOutputStream fileOutputStream = new FileOutputStream("text.txt");
             FileChannel fileChannel = fileOutputStream.getChannel()) {
             
            ByteBuffer buffer = StandardCharsets.UTF_8.encode("write");

            while (buffer.hasRemaining()) {
                int write = fileChannel.write(buffer);
                System.out.println("write bytes: " + write);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

4. 关闭FileChannel

channel在用完之后必须关闭,如果调用了【FileInputStream】、【FileOutputStream】、【RandomAccessFile】的close方法,会间接调用FileChannel的Close方法。

因此,最好使用Java try-with-resources语句。

5. transferTo

将数据从一个文件传输到另一个文件。

  • 参数一:从源文件的哪个位置开始传输;
  • 参数二:传输到源文件的哪个位置结束;
  • 参数三:将源文件的内容传输到哪里。

5.1 文件不超过2g,可以一次传输完成

0表示从源文件的起始位置开始传输;

from.size()表示要传输源文件的所有数据;

to表示数据传输到哪里;

  1. transferTo方法的效率,比java BIO的效率高,因为底层使用了操作系统的零拷贝进行了优化;
  2. 缺点是 一次最多传输2g的数据;如果源文件超过2g,只调用一次transferTo方法,目标文件也就只有2g。因此,要想完整的传输2g以上的文件,就需要多次传输。
java 复制代码
public static void main(String[] args) {
        try (FileInputStream fileInputStream = new FileInputStream("from.txt");
             FileOutputStream fileOutputStream = new FileOutputStream("to.txt");
             FileChannel from = fileInputStream.getChannel();
             FileChannel to = fileOutputStream.getChannel()) {

            from.transferTo(0, from.size(), to);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

5.2 文件超过2g,则多次传输

java 复制代码
public static void main(String[] args) {
        try (FileInputStream fileInputStream = new FileInputStream("from.txt");
             FileOutputStream fileOutputStream = new FileOutputStream("to.txt");
             FileChannel from = fileInputStream.getChannel();
             FileChannel to = fileOutputStream.getChannel()) {

            // size,源文件的大小
            long size = from.size();

            // left,表示剩余的需要传输的数据的大小,初始值为源文件的size
            long left = size;

            // left > 0,表示还有内容没有传输完成
            while (left > 0) {
                
                // transferTo方法的返回值,表示已经传输的数据的大小
                long transferred = from.transferTo(size - left, left, to);
                
                // 剩余需要传输的数据大小 - 已传输的数据的大小
                left = left - transferred;
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
相关推荐
那个松鼠很眼熟w3 小时前
【NIO】2. ByteBuffer的基本使用
nio
今天的砖头有点烫手啊5 天前
Java IO/NIO/AIO 演进:从 BIO 到 Netty 的底层逻辑
java·jvm·nio
名字还没想好☜23 天前
Java NIO ByteBuffer 实战:flip/clear/compact 三个绕晕人的方法与 position/limit 心智模型
java·开发语言·后端·spring·nio
滕州市燕猫虎计算机科技工作室个体工商户23 天前
java.nio.ByteBuffer的几个常用方法
nio
CodeStats1 个月前
【Java文件IO】Java 文件 IO 体系深度拆解(中):NIO API 详解、Files 与 FileChannel 巅峰对决
java·io·文件·nio·零拷贝·mmap
evans在进步1 个月前
Tomcat NIO 工作原理详解:从 Acceptor、Poller 到容器处理链
java·tomcat·nio
二炮手亮子1 个月前
从 BIO、NIO、AIO 到多路复用与 Netty
策略模式·nio
Bill FANG2 个月前
从原生Java NIO到Netty重构:充电桩Socket服务端高性能改造实战
java·重构·nio
gugucoding2 个月前
34.【Java】I/O流(下):NIO与文件操作
java·python·nio