NIO基础——文件编程
2021/12/25 20:37:14
本文主要是介绍NIO基础——文件编程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
上一次聊了NIO基础中的三大组建和BetyBuffer的东西。
这次就聊文件编程 FileChannel
一、FileChannel
工作模式
FileChannel只能工作在阻塞模式下,因此它并不能配合Selector(选择器)来使用。
获取
不能直接打开FileChannel,必须通过FileInputStream、FileOutputStream
或者RandomAccessFile来获取FileChannel实例,它们都有getChannel()方法。
- 通过FileInputStream创建的channel只能读。
- 通过FileOutputStream创建的channel只能写。
- 通过RandomAccessFile创建的channel能否读写,根据构造 RandomAccessFile时的读写模式决定。
读取
FileChannel提供了read()方法来读取数据到缓冲区中。
int readIndex = fileChannel.read(byteBuffer);
read方法的返回值代表读取到的数据位置,当返回值等于“-1”时说明读取到了文件末尾处。可以凭此判断数据是否读取完毕了。
写入
FileChannel提供了 write 方法来写入数据到通道中。
但是因为channel的大小有限制,所以 write 方法并不能保证一次将缓冲区中的内容全部写入 channel。必须需要按照以下规则进行写入:
// 通过hasRemaining()方法查看缓冲区中是否还有数据未写入到通道中 while(buffer.hasRemaining()) { channel.write(buffer); }
关闭
channel使用结束后必须关闭,可以通过 close 方法进行通道的关闭。
最好使用下面的方法获取channel,避免因为一些意外导致资源无法正常关闭。
// 获取FileChannel try (FileChannel channel = new FileInputStream("hello.txt").getChannel()) { // 业务代码 } } catch (IOException e) { // ... }
位置
通过channel.position()方法可以数据读取的位置。
long pos = channel.position();
可以传入一个long类型的值来设置channel中position的值。
long newPos = 1000 channel.position(newPos);
大小
通过channel.size()方法可以获取小大。
强制写入
操作系统出于性能考虑,会将数据先缓存,并不是立刻写入磁盘。可以调用 force(true) 方法将文件内容和元数据(文件的权限信息)立刻写入磁盘中。
但是该方法会影响性能。
二、两个Channel传输数据
transferTo方法
使用transferTo方法可以快速,高校的将一个channel中的数据传输给另一个channel(底层会零拷贝进行优化),但是一次最多只能传输2G的数据。
transferTo需要传入三个参数:
- position:传输的起始位置。
- count:传输的数据量。
- target:目标channel。
方法返回值代表已经传输的数据量。
public class FileChannelTransferToTest { public static void main(String[] args) { try (FileChannel inputChannel = new FileInputStream("hello.txt").getChannel(); FileChannel outputChannel = new FileInputStream("hello-out.txt").getChannel()) { long transferToSize = inputChannel.transferTo(0, inputChannel.size(), outputChannel); } catch (IOException e) { e.printStackTrace(); } } }
当文件大小大于2G时如何处理?
public class FileChannelTransferToTest { public static void main(String[] args) { try (FileChannel inputChannel = new FileInputStream("hello.txt").getChannel(); FileChannel outputChannel = new FileInputStream("hello-out.txt").getChannel()) { // 获取inputChannel的大小 long inputChannelSize = inputChannel.size(); // left代表剩余多少字节 for (long left = inputChannelSize; left > 0; ) { left -= inputChannel.transferTo((inputChannelSize - left), left, outputChannel); } } catch (IOException e) { e.printStackTrace(); } } }
三、Paths&Paths
- Path:文件路径。
- Paths:工具类,获取Path实例。
// 相对路径,使用usr.dir环境变量来定位1.txt Path path1 = Paths.get("1.txt"); // path1 = 1.txt System.out.println(path1); // 绝对路径,代表 d:\1.txt Path path2 = Paths.get("d:\\1.txt"); // path2 = d:\1.txt System.out.println(path2); // 绝对路径,代表 d:\1.txt Path path3 = Paths.get("d:/1.txt"); // path3 = d:\1.txt System.out.println(path3); // 代表 d:\data\demo Path path4 = Paths.get("d:\\data","demo"); // path4 = d:\data\demo System.out.println(path4);
- ·:当前路径下
- ··:上一级路径下
例如现有目录如下
d: |- data |- demo |- a |- b
Path path5 = Paths.get("d:\\data\\demo\\a\\..\\b"); // path5 = d:\data\demo\a\..\b System.out.println(path5); // path5.normalize() = d:\data\demo\b System.out.println(path5.normalize());
normalize方法会分析并返回最终的路径。
四、Files
检查(exists)
检测文件是否存在
// 检查文件是否存在 Path path = Paths.get("hello/1.txt"); boolean exists = Files.exists(path);
创建(createDirectory&createDirectories)
创建一级目录
- 如果目录已经存在,会抛出FileAlreadyExistsException异常。
- 不能创建多级目录,否则会抛出NoSuchFileException异常。
// 创建一个一级目录 Path path2 = Paths.get("hello/a"); // 如果目录已经存在,会抛异常:FileAlreadyExistsException // 如果是创建多级目录,会抛异常:NoSuchFileException Files.createDirectory(path2);
创建多级目录
// 创建多级目录 Path path3 = Paths.get("hello/a/a-1"); Files.createDirectories(path3);
拷贝(copy)
- 如果文件已经存在,会抛出FileAlreadyExistsException异常。
- 如果希望用source覆盖掉target,需要用StandardCopyOption来控制。
// 拷贝 Path path4 = Paths.get("hello/source.txt"); Path path5 = Paths.get("hello/target.txt"); Files.copy(path4,path5, StandardCopyOption.REPLACE_EXISTING);
移动(move)
// 拷贝 Path path4 = Paths.get("hello/source.txt"); Path path5 = Paths.get("hello/target.txt"); // StandardCopyOption.ATOMIC_MOVE保存文件移动的原子性 Files.move(path4,path5,StandardCopyOption.ATOMIC_MOVE);
删除(delete)
删除文件
// 删除文件 Path path4 = Paths.get("hello/source.txt"); Files.delete(path4);
如果文件不存在,会抛出NoSuchFileException异常。
删除文件夹
// 删除文件夹 Path path5 = Paths.get("hello/a"); Files.delete(path5);
如果文件夹里还存在内容,会抛出DirectoryNotEmptyException异常
遍历(walkFileTree)
walkFileTree方法需要传入两个参数:
-
Path:文件的路径。
-
FileVisitor:文件访问器。(访问者模式)
- 这里使用实现类SimpleFileVisitor,其有四个方法:
- preVisitDirectory:访问目录前的操作
- visitFile:访问文件的操作
- visitFileFailed:访问文件失败时的操作
- postVisitDirectory:访问目录后的操作
- 这里使用实现类SimpleFileVisitor,其有四个方法:
Files.walkFileTree(Paths.get("F:\\dev\\Java\\jdk1.8.0_131"), new SimpleFileVisitor<Path>(){ // 访问目录前的操作 @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { return super.preVisitDirectory(dir, attrs); } // 访问文件的操作 @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { return super.visitFile(file, attrs); } // 访问文件失败时的操作 @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { return super.visitFileFailed(file, exc); } // 访问目录后的操作 @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { return super.postVisitDirectory(dir, exc); } });
本篇文章根据在学习《黑马程序员Netty实战》时所做的笔记总结。
这篇关于NIO基础——文件编程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-01后台管理开发学习:新手入门指南
- 2024-11-01后台管理系统开发学习:新手入门教程
- 2024-11-01后台开发学习:从入门到实践的简单教程
- 2024-11-01后台综合解决方案学习:从入门到初级实战教程
- 2024-11-01接口模块封装学习入门教程
- 2024-11-01请求动作封装学习:新手入门教程
- 2024-11-01登录鉴权入门:新手必读指南
- 2024-11-01动态面包屑入门:轻松掌握导航设计技巧
- 2024-11-01动态权限入门:新手必读指南
- 2024-11-01动态主题处理入门:新手必读指南