NIO之FileChannel解读
【摘要】 编辑基本概述FileChannel 类可以实现常用的 read,write 以及 scatter/gather 操作,同时它也提 供了很多专用于文件的新方法。这些方法中的许多都是我们所熟悉的文件操作。编辑下面是一个使用 FileChannel 读取数据到 Buffer 中的示例 :RandomAccessFile既可以读取文件内容,也可以向文件输出数据。同时RandomAccessFi...
基本概述
FileChannel 类可以实现常用的 read,write 以及 scatter/gather 操作,同时它也提 供了很多专用于文件的新方法。这些方法中的许多都是我们所熟悉的文件操作。
下面是一个使用 FileChannel 读取数据到 Buffer 中的示例 :
RandomAccessFile既可以读取文件内容,也可以向文件输出数据。同时RandomAccessFile支持“随机访问”的方式,程序快可以直接跳转到文件的任意地方来读写数据。
Buffer 通常的操作
将数据写入缓冲区
调用 buffer.flip() 反转读写模式
从缓冲区读取数据
调用 buffer.clear() 或 buffer.compact() 清除缓冲区内容
打开 FileChannel
在使用 FileChannel 之前,必须先打开它。但是,我们无法直接打开一个 FileChannel,需要通过使用一个 InputStream、OutputStream 或 RandomAccessFile 来获取一个 FileChannel 实例。下面是通过 RandomAccessFile 打开 FileChannel 的示例:
从 FileChannel 读取数据
调用多个 read()方法之一从 FileChannel 中读取数据。如:
首先,分配一个 Buffer。从 FileChannel 中读取的数据将被读到 Buffer 中。然后,调 用 FileChannel.read()方法。该方法将数据从 FileChannel 读取到 Buffer 中。read() 方法返回的 int 值表示了有多少字节被读到了 Buffer 中。如果返回-1,表示到了文件 末尾。
向 FileChannel 写数据
使用 FileChannel.write()方法向 FileChannel 写数据,该方法的参数是一个 Buffer。
注意 FileChannel.write()是在 while 循环中调用的。因为无法保证 write()方法一次能向 FileChannel 写入多少字节,因此需要重复调用 write()方法,直到 Buffer 中已经没 有尚未写入通道的字节。
关闭 FileChannel
用完 FileChannel 后必须将其关闭。如:
FileChannel 的 position 方法
有时可能需要在 FileChannel 的某个特定位置进行数据的读/写操作。可以通过调用 position()方法获取 FileChannel 的当前位置。也可以通过调用 position(long pos)方 法设置 FileChannel 的当前位置。
如果将位置设置在文件结束符之后,然后试图从文件通道中读取数据,读方法将返回- 1 (文件结束标志)。
如果将位置设置在文件结束符之后,然后向通道中写数据,文件将撑大到当前位置并 写入数据。这可能导致“文件空洞”,磁盘上物理文件中写入的数据间有空隙。
FileChannel 的 size 方法
FileChannel 实例的 size()方法将返回该实例所关联文件的大小。如:
FileChannel 的 truncate 方法
可以使用 FileChannel.truncate()方法截取一个文件。截取文件时,文件将中指定长度 后面的部分将被删除。如:
这个例子截取文件的前 1024 个字节。
FileChannel 的 force 方法
FileChannel.force()方法将通道里尚未写入磁盘的数据强制写到磁盘上。出于性能方面的考虑,操作系统会将数据缓存在内存中,所以无法保证写入到 FileChannel 里的 数据一定会即时写到磁盘上。要保证这一点,需要调用 force()方法。
force()方法有一个 boolean 类型的参数,指明是否同时将文件元数据(权限信息等) 写到磁盘上。
FileChannel 的 transferTo 和 transferFrom 方法
通道之间的数据传输:
如果两个通道中有一个是 FileChannel,那你可以直接将数据从一个 channel 传输到 另外一个 channel。
(1)transferFrom()方法
FileChannel 的 transferFrom()方法可以将数据从源通道传输到 FileChannel 中(译 者注:这个方法在 JDK 文档中的解释为将字节从给定的可读取字节通道传输到此通道 的文件中)。下面是一个 FileChannel 完成文件间的复制的例子:
方法的输入参数 position 表示从 position 处开始向目标文件写入数据,count 表示最 多传输的字节数。如果源通道的剩余空间小于 count 个字节,则所传输的字节数要小 于请求的字节数。此外要注意,在 SoketChannel 的实现中,SocketChannel 只会传 输此刻准备好的数据(可能不足 count 字节)。因此,SocketChannel 可能不会将请 求的所有数据(count 个字节)全部传输到FileChannel 中。
(2)transferTo()方法
transferTo()方法将数据从 FileChannel 传输到其他的 channel 中。
Scatter/Gather
Java NIO 开始支持 scatter/gather,scatter/gather 用于描述从 Channel 中读取或 者写入到 Channel 的操作。
分散(scatter)从 Channel 中读取是指在读操作时将读取的数据写入多个 buffer 中。 因此,Channel 将从 Channel 中读取的数据“分散(scatter)”到多个 Buffer 中。
聚集(gather)写入 Channel 是指在写操作时将多个 buffer 的数据写入同一个 Channel,因此,Channel 将多个 Buffer 中的数据“聚集(gather)”后发送到 Channel。
scatter / gather 经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头 和消息体组成的消息,你可能会将消息体和消息头分散到不同的 buffer 中,这样你可 以方便的处理消息头和消息体。
Scattering Reads
Scattering Reads 是指数据从一个 channel 读取到多个 buffer 中。如下图描述:
注意 buffer 首先被插入到数组,然后再将数组作为 channel.read() 的输入参数。 read()方法按照 buffer 在数组中的顺序将从 channel 中读取的数据写入到 buffer,当 一个 buffer 被写满后,channel 紧接着向另一个 buffer 中写。
Scattering Reads 在移动下一个 buffer 前,必须填满当前的 buffer,这也意味着它 不适用于动态消息(译者注:消息大小不固定)。换句话说,如果存在消息头和消息体, 消息头必须完成填充(例如 128byte),Scattering Reads 才能正常工作。
Gathering Writes
Gathering Writes 是指数据从多个 buffer 写入到同一个 channel。如下图描述:
buffers 数组是 write()方法的入参,write()方法会按照 buffer 在数组中的顺序,将数 据写入到 channel,注意只有 position 和 limit 之间的数据才会被写入。因此,如果 一个 buffer 的容量为 128byte,但是仅仅包含 58byte 的数据,那么这 58byte 的数 据将被写入到 channel 中。因此与 Scattering Reads 相反,Gathering Writes 能较 好的处理动态消息。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)