ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

NIO基础——文件编程

2021-12-25 20:34:55  阅读:175  来源: 互联网

标签:文件 NIO Paths 编程 Path get txt hello channel


上一次聊了NIO基础中的三大组建和BetyBuffer的东西。
这次就聊文件编程 FileChannel

一、FileChannel

工作模式

FileChannel只能工作在阻塞模式下,因此它并不能配合Selector(选择器)来使用。

获取

不能直接打开FileChannel,必须通过FileInputStreamFileOutputStream
或者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:访问目录后的操作
        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,Paths,编程,Path,get,txt,hello,channel
来源: https://blog.csdn.net/LIKEGAKKI/article/details/122144314

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有