Java NIO:FileChannel
一、FileChannel的优势
FileChannel 的优点包括:
- 在文件的特定位置读取和写入;
- 将文件的一部分直接加载到内存中,这样效率最高;
- 可以以更快的速度将文件数据从一个通道传输到另一个通道;
- 可以锁定文件的一部分以限制其他线程访问;
- 为了避免数据丢失,我们可以强制将更新的文件立即写入存储。
二、使用FileChannel打开文件
1. 使用 RandomAccessFile 打开 FileChannel :
1 RandomAccessFile reader = new RandomAccessFile(file, "r"); 2 FileChannel channel = reader.getChannel();
关闭 RandomAccessFile 也会关闭关联的通道。
模式“r”表示通道仅开发“读取”,模式“rw”是开放“读取”和“写入”。此外还有:
“rw”—— 对文件的内容的每次更新,并且在你关闭文件之前不会更改修改日期。
“rws”—— 对文件的内容和文件的修改日期(元数据)的每一次更新都同步写入到底层存储设备中;
“rwd”—— 对文件的内容的每一次更新都同步写入底层存储器中,但在文件关闭之前修改日期(元数据)可能不会更改。
模式“rwd” 的写入速度比“rw” 慢得多,而 “rws” 又更慢得多。使用“rwd”可用于减少执行的I/O操作的数量,使用“rws”需要更新文件内容及其要写入的元数据,这起码要进行一次低级I/O操作。
PS:文件还可能有一些相关的元数据,例如访问权限、内容类型和上次修改时间。
2. 使用 FileInputStream 打开 FileChannel :
1 FileInputStream fin= new FileInputStream(file); 2 FileChannel channel = fin.getChannel();
关闭 FileInputStream 也会关闭与其关联的通道。
三、从FileChannel读取数据
1 ByteBuffer buff = ByteBuffer.allocate(1024); 2 int noOfBytesRead = channel.read(buff); 3 String fileContent = new String(out.toByteArray(), StandardCharsets.UTF_8);
分配一个Buffer,从FileChannel中读取数据到这个Buffer中。
四、使用FileChannel写入文件
1. 使用 RandomAccessFile 打开 FileChannel ,模式为“rw”,使用 ByteBuffer 来存储数据并写入文件。
1 RandomAccessFile writer = new RandomAccessFile(file, "rw"); 2 FileChannel channel = writer.getChannel(); 3 ByteBuffer buff = ByteBuffer.wrap("Writing...".getBytes(StandardCharsets.UTF_8)); 4 channel.write(buff);
2. 使用 FileOutputStream 打开 FileChannel 。
1 FileOutputStream fout = new FileOutputStream(file); 2 FileChannel channel = fout.getChannel();
五、获取位置
在 FileChannel 的某个特定位置进行数据的读 / 写操作。可以通过调用 position() 方法获取 FileChannel 的当前位置。
1 long originalPosition = channel.position(); 2 System.out.println(originalPosition); 3 channel.position(6); 4 long currentPosition = channel.position(); 5 System.out.println(currentPosition);
六、获取文件大小
FileChannel.size() 方法将返回该实例所关联文件的大小,单位是字节。
1 RandomAccessFile reader = new RandomAccessFile(file, "r"); 2 FileChannel channel = reader.getChannel(); 3 System.out.println(channel.size()); 4 channel.close(); 5 reader.close();
七、截断文件
使用 FileChannel.truncate 方法将文件截断为给定的字节大小(截取前 n 个字节)。
1 String input = "this is a test input"; 2 FileOutputStream fos = new FileOutputStream("src/main/resources/FILE.txt"); 3 FileChannel channel = fos.getChannel(); 4 5 ByteBuffer buffer = ByteBuffer.wrap(input.getBytes()); 6 channel.write(buffer); 7 buffer.flip(); 8 9 channel.truncate(5); 10 System.out.println(channel.size()); 11 12 fos.close(); 13 channel.close();
八、强制文件更新到底层存储
操作系统可能出于性能原因缓存文件更改,如果系统崩溃,数据可能会丢失。要强制文件内容和元数据连续写入磁盘,我们可以使用 force 方法:
1 channel.force(true);
出于性能方面的考虑,操作系统会将数据缓存在内存中,所以无法保证写入到 FileChannel 里的数据一定会即时写到磁盘上。