FileChannel指南
推荐关注公众号:锅外的大佬
每日推送国外技术好文,帮助每位开发者更优秀地成长
原文链接:https://www.baeldung.com/java-filechannel
作者:baeldung
译者:Leesen
1.概述
在这篇速学教程中,我们将研究Java NIO
库中提供的FileChannel
类,讨论如何使用FileChannel
和ByteBuffer
读写数据,探讨使用FileChannel
以及其他文件操作特性的优点。
2.FileChannel的优点
FileChannel
的优点包括:
- 在文件特定位置进行读写操作
- 将文件一部分直接加载到内存,这样效率更高
- 以更快的速度将文件数据从一个通道传输到另一个通道
- 锁定文件的某一部分来限制其他线程访问
- 为了避免数据丢失,强制立即将更新写入文件并存储
3.FileChannel读操作
当我们读取一个大文件时,FileChannel
比标准I/O
执行得更快。需要注意,虽然FileChannel
是Java NIO
的一部分,但是FileChannel
操作是阻塞的,并且没有非阻塞模式。
3.1.使用FileChannel读取文件
先了解如何使用FileChannel
读取一个文件,该文件包含:
Hello world
下面测试读取文件,并检查是否ok:
@Test
public void givenFile_whenReadWithFileChannelUsingRandomAccessFile_thenCorrect()
throws IOException {
try (RandomAccessFile reader = new RandomAccessFile("src/test/resources/test_read.in", "r");
FileChannel channel = reader.getChannel();
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
int bufferSize = 1024;
if (bufferSize > channel.size()) {
bufferSize = (int) channel.size();
}
ByteBuffer buff = ByteBuffer.allocate(bufferSize);
while (channel.read(buff) > 0) {
out.write(buff.array(), 0, buff.position());
buff.clear();
}
String fileContent = new String(out.toByteArray(), StandardCharsets.UTF_8);
assertEquals("Hello world", fileContent);
}
}
这里使用FileChannel
、RandomAccessFile
和ByteBuffer
从文件中读取字节。还应该注意,多个并发线程可以安全地使用FileChannel
。但是,每次只允许一个线程执行涉及更新通道位置(channel position
)或更改其文件大小的操作。这会阻止其他试图执行类似操作的线程,直到前一个操作完成。
但是,显式提供通道位置的操作可以并发运行且不会被阻塞。
3.2.打开FileChannel
为了使用FileChannel
读取文件,我们必须打开它(Open FileChannel
)。看看如何使用RandomAccessFile
打开FileChannel
:
RandomAccessFile reader = new RandomAccessFile(file, "r");
FileChannel channel = reader.getChannel();
模式“r”表示通道仅为“只读“,注意,关闭RandomAccessFile
也将关闭与之关联的通道。
接下来,使用FileInputStream
打开一个FileChannel
来读取文件:
FileInputStream fin= new FileInputStream(file);
FileChannel channel = fin.getChannel();
同样的,关闭FileInputStream
也会关闭与之相关的通道。
3.3.从FileChannel中读取数据
为了读取数据,我们可以使用只读模式。接下来看看如何读取字节序列,我们将使用ByteBuffer
来保存数据:
ByteBuffer buff = ByteBuffer.allocate(1024);
int noOfBytesRead = channel.read(buff);
String fileContent = new String(buff.array(), StandardCharsets.UTF_8);
assertEquals("Hello world", fileContent);
然后,我们将看到如何从文件某个位置开始读取一个字节序列:
ByteBuffer buff = ByteBuffer.allocate(1024);
int noOfBytesRead = channel.read(buff, 5);
String fileContent = new String(buff.array(), StandardCharsets.UTF_8);
assertEquals("world", fileContent);
我们应该注意:需要使用字符集(Charset)将字节数组解码为字符串
我们指定原始编码字节的字符集。没有它,我们可能会以断章取义的文字结束。特别是像UTF-8
和UTF-16
这样的多字节编码可能无法解码文件的任意部分,因为一些多字节字符可能是不完整的。
4.FileChannel写操作
4.1.使用FileChannel写入文件
我们来探究下如何使用FileChannel
写:
@Test
public void whenWriteWithFileChannelUsingRandomAccessFile_thenCorrect()
throws IOException {
String file = "src/test/resources/test_write_using_filechannel.txt";
try (RandomAccessFile writer = new RandomAccessFile(file, "rw");
FileChannel channel = writer.getChannel()){
ByteBuffer buff = ByteBuffer.wrap("Hello world".getBytes(StandardCharsets.UTF_8));
channel.write(buff);
// verify
RandomAccessFile reader = new RandomAccessFile(file, "r");
assertEquals("Hello world", reader.readLine());
reader.close();
}
}
4.2.打开FileChannel
要使用FileChannel
写入文件,必须先打开它。使用RandomAccessFile
打开一个FileChannel
:
RandomAccessFile writer = new RandomAccessFile(file, "rw");
FileChannel channel = writer.getChannel();
模式“rw”表示通道为“读写”。
使用FileOutputStream
打开FileChannel
:
FileOutputStream fout = new FileOutputStream(file);
FileChannel channel = fout.getChannel();
4.3.FileChannel写入数据
使用FileChannel
写数据,可以使用其中的某个写方法。
我们来看下如何写一个字节序列,使用ByteBuffer
来存储数据:
ByteBuffer buff = ByteBuffer.wrap("Hello world".getBytes(StandardCharsets.UTF_8));
channel.write(buff);
接下来,我们将看到如何从文件某个位置开始写一个字节序列:
ByteBuffer buff = ByteBuffer.wrap("Hello world".getBytes(StandardCharsets.UTF_8));
channel.write(buff, 5);
5.当前位置
FileChannel
允许我们获得和改变读或写的位置(position
)。获得当前的位置:
long originalPosition = channel.position();
设置位置:
channel.position(5);
assertEquals(originalPosition + 5, channel.position());
6.获取文件大小
使用FileChannel.size
方法获取文件大小(以字节为单位):
@Test
public void whenGetFileSize_thenCorrect()
throws IOException {
RandomAccessFile reader = new RandomAccessFile("src/test/resources/test_read.in", "r");
FileChannel channel = reader.getChannel();
// the original file size is 11 bytes.
assertEquals(11, channel.size());
channel.close();
reader.close();
}
7.截断文件
使用FileChannel.truncate
方法将文件截断为给定的大小(以字节为单位):
@Test
public void whenTruncateFile_thenCorrect() throws IOException {
String input = "this is a test input";
FileOutputStream fout = new FileOutputStream("src/test/resources/test_truncate.txt");
FileChannel channel = fout.getChannel();
ByteBuffer buff = ByteBuffer.wrap(input.getBytes());
channel.write(buff);
buff.flip();
channel = channel.truncate(5);
assertEquals(5, channel.size());
fout.close();
channel.close();
}
8.强制更新
由于性能原因,操作系统可能缓存文件更改,如果系统崩溃,数据可能会丢失。要强制文件内容和元数据不断写入磁盘,我们可以使用force
方法:
channel.force(true);
仅当文件存储在本地设备上时,才能保证该方法有效。
9.将文件部分加载到内存
使用FileChannel.map
方法将文件的部分加载到内存中。使用FileChannel.MapMode.READ_ONLY
以只读模式打开文件:
@Test
public void givenFile_whenReadAFileSectionIntoMemoryWithFileChannel_thenCorrect() throws IOException {
try (RandomAccessFile reader = new RandomAccessFile("src/test/resources/test_read.in", "r");
FileChannel channel = reader.getChannel();
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
MappedByteBuffer buff = channel.map(FileChannel.MapMode.READ_ONLY, 6, 5);
if(buff.hasRemaining()) {
byte[] data = new byte[buff.remaining()];
buff.get(data);
assertEquals("world", new String(data, StandardCharsets.UTF_8));
}
}
}
类似地,可以使用FileChannel.MapMode.READ_WRITE
以读写模式打开文件。还可以使用FileChannel.MapMode.PRIVATE
模式,该模式下,更改不应用于原始文件。
10.锁定文件部分
来看下如何锁定文件某一部分,使用FileChannel.tryLock
方法阻止对文件某一部分进行高并发访问。
@Test
public void givenFile_whenWriteAFileUsingLockAFileSectionWithFileChannel_thenCorrect() throws IOException {
try (RandomAccessFile reader = new RandomAccessFile("src/test/resources/test_read.in", "rw");
FileChannel channel = reader.getChannel();
FileLock fileLock = channel.tryLock(6, 5, Boolean.FALSE )){
//do other operations...
assertNotNull(fileLock);
}
}
tryLock
方法尝试获取文件部分(file section
)上的锁。如果请求的文件部分已被另一个线程阻塞,它将抛出一个OverlappingFileLockException
异常。此方法还接受Boolean
参数来请求共享锁或独占锁。
我们应该注意到,有些操作系统可能不允许共享锁,默认情况下是独占锁。
11.FileChannel关闭
最后,当使用FileChannel时,必须关闭它。在示例中,我们使用了try-with-resources
。
如果有必要,我们可以直接使用FileChannel.close
方法:
channel.close();
12.总结
在本教程中,我们了解了如何使用FileChannel
读取和写入文件。此外,我们还研究了如何读取和更改文件大小及其当前读/写位置,并研究了如何在并发应用程序或数据关键应用程序中使用FileChannel
。
与往常一样,示例的源代码可以在GitHub上找到。
FileChannel指南的更多相关文章
- Flume FileChannel优化(扩展)实践指南
本文系微博运维数据平台(DIP)在Flume方面的优化扩展经验总结,在使用Flume FileChannel的场景下将吞吐率由10M/s~20M/s提升至80M/s~90M/s,分为四个部分进行介绍: ...
- Flume NG Getting Started(Flume NG 新手入门指南)
Flume NG Getting Started(Flume NG 新手入门指南)翻译 新手入门 Flume NG是什么? 有什么改变? 获得Flume NG 从源码构建 配置 flume-ng全局选 ...
- Netty权威指南
Netty权威指南(异步非阻塞通信领域的经典之作,国内首本深入剖析Netty的著作,全面系统讲解原理.实战和源码,带你完美进阶Netty工程师.) 李林锋 著 ISBN 978-7-121-233 ...
- 【Flume NG用户指南】(1)设置
作者:周邦涛(Timen) Email:zhoubangtao@gmail.com 转载请注明出处: http://blog.csdn.net/zhoubangtao/article/details ...
- 【Flume NG用户指南】(2)构造
作者:周邦涛(Timen) Email:zhoubangtao@gmail.com 转载请注明出处: http://blog.csdn.net/zhoubangtao/article/details ...
- 【翻译】Flume 1.8.0 User Guide(用户指南) Processors
翻译自官网flume1.8用户指南,原文地址:Flume 1.8.0 User Guide 篇幅限制,分为以下5篇: [翻译]Flume 1.8.0 User Guide(用户指南) [翻译]Flum ...
- 【翻译】Flume 1.8.0 User Guide(用户指南) Channel
翻译自官网flume1.8用户指南,原文地址:Flume 1.8.0 User Guide 篇幅限制,分为以下5篇: [翻译]Flume 1.8.0 User Guide(用户指南) [翻译]Flum ...
- 【翻译】Flume 1.8.0 User Guide(用户指南)
翻译自官网flume1.8用户指南,原文地址:Flume 1.8.0 User Guide 篇幅限制,分为以下5篇: [翻译]Flume 1.8.0 User Guide(用户指南) [翻译]Flum ...
- 《Netty权威指南》
<Netty权威指南> 基本信息 作者: 李林锋 出版社:电子工业出版社 ISBN:9787121233432 上架时间:2014-5-29 出版日期:2014 年6月 开本:16开 页码 ...
随机推荐
- Java-basic-7-面向对象
继承 在Java中,每个子类只能有一个父类,但可以继承多个接口. 子类继承父类,类定义的时候用extends. 继承接口,用implements. 重写 声明为final的方法不能被重写. 声明为st ...
- CentOS 7 配置OpenCL环境(安装NVIDIA cuda sdk、Cmake、Eclipse CDT)
序 最近需要在Linux下进行一个OpenCL开发的项目,现将开发环境的配置过程记录如下,方便查阅. 完整的环境配置需要以下几个部分: 安装一个OpenCL实现,基于硬件,选择NVIDIA CUDA ...
- SmartGit 30天评估期结束解决办法
smartgit 需要输入序列号解决办法: 1.找到路径: %APPDATA%\syntevo\SmartGit\<main-smartgit-version> 然后删除: setting ...
- local search——配图
- markdown快捷键
分组 功能 操作 快捷键 设置标题 一级标题 Heading1 Ctrl+1 二级标题 Heading2 Ctrl+2 三级标题 Heading3 Ctrl+3 四级标题 Heading4 Ctrl+ ...
- hdu4861 我只能说这是找规律=.=
先说明一下题意,因为开始我就没太读懂,感觉作者不是没交代清楚就是让做题的人自己去领悟,开始我不知道球是可以随便选的,然后那个关系式到底是最后一个数模p,还是整体模P........最后确定是整体模P ...
- 使用百度siteapp开发网站的App-(IOS和Android版本)
介绍 之前写了个把百度云作文网站文件服务器.一些园友的评论不错.不过我似乎把意思弄错了! 我用的百度云的SVN环境! 现在不少人都做web开发.不管你是什么语言编写的(jsp,php,asp.net ...
- 一个Work Stealing Pool线程池的实现
一.一般来说实现一个线程池主要包括以下几个组成部分: 1)线程管理器 :用于创建并管理线程池 . 2)工作线程 :线程池中实际执行任务的线程 . 在初始化线程时会预先创建好固定数目的线程在池中 ,这些 ...
- 【Luogu】P3232游走(高斯消元解概率)
题目链接 参见远航之曲dalao的题解,我再写一遍的话就没啥意思了. #include<cstdio> #include<cstring> #include<algori ...
- 【P2387】魔法森林(SPFA非正解)
题目链接 不会LCTqwq,看题解似乎SPFA也可以. 把边按a排序,从小到大每加一条边就以b为距离跑一遍SPFA,类似于Kruskal的想法吧…… 貌似是个暴力 (luoguLCT模块的题我都快通过 ...