Java NIO之文件处理

Wesley13
• 阅读 664

程序要操作本地操作系统的一个文件,可以分为以下三个部分:

  • 对文件位置的操作
  • 对文件的操作
  • 对文件内容的操作

其中,对文件内容的操作在 Java NIO之Channel 中已经有了介绍,通过FileChannel可以读/写文件内容。本文不做重复介绍,详情参考我的另一篇文章: Java NIO之Channel 。

1. 对文件位置的操作

在java.io中,有一个File类可以对文件位置、文件进行操作,对这两种操作没有区分开。对于操作文件位置的能力来说,File类不能很好地遍历目录,对于文件位置的操作非常不灵活,只能从父目录开始,一层一层地访问。

在java.nio中,提供了一个Path接口,这个接口专门处理文件位置,对文件本身操作不做处理。这个Path所代表的位置可以真实存在,也可以暂时先不存在。如果这个位置不存在,试图对文件、文件内容进行操作的时候,就会报错;但是对文件位置进行操作没有任何问题。

1.1 获取Path实例

Path是一个接口,不能直接实例化,最常用的实例化方法如下:

Path listing = Paths.get("Y:/path/usecase/example.txt");

对于Path的实例化,可以用绝对路径,也可以用相对路径,相对路径是指相对于代码运行时所在位置。如果文件位置在程序外的固定位置,就应该用绝对路径。如果文件位置在程序内,并且程序的运行时位置可能会变动,就应该用相对位置。

1.2 获取Path信息

Path接口提供了获取当前文件/目录名、父目录、根目录、子目录以及Path的元素个数:

Path listing = Paths.get("Y:/path/usecase/example.txt");
        
System.out.println("文件名称:"+listing.getFileName());
System.out.println("这个Path的元素个数:"+listing.getNameCount());
System.out.println("父路径:"+listing.getParent());
System.out.println("根路径:"+listing.getRoot());
System.out.println("从根路径开始数起,2个元素深度的子路径:"+listing.subpath(0, 2));

文件名称:example.txt
这个Path的元素个数:3
父路径:Y:\path\usecase
根路径:Y:\
从根路径开始数起,2个元素深度的子路径:path\usecase

如果你想通过当前位置,找到一个相对位置的文件,就非常方便,这在java.io中是做不到的。 

1.3 找到Path的真实路径

首先,加入创建Path实例的时候,用的是相对路径,有时间我们需要知道绝对路径在哪:

Path listing1 = Paths.get("example1.txt");
System.out.println("相对路径:"+listing1);
System.out.println("绝对路径:"+listing1.toAbsolutePath());

然后有时候我们会遇到用一个点代表当前目录的写法,这个写法在Path构建中是没有必要的。“./example2.txt”和“example2.txt”实际上代表的意思一样。遇到这种情况,在Path构建中就必须先正常化:

Path listing2 = Paths.get("./example1.txt");
System.out.println("相对路径:"+listing2);
System.out.println("绝对路径:"+listing2.toAbsolutePath());
Path listing3 = Paths.get("./example1.txt").normalize();
System.out.println("相对路径:"+listing3);
System.out.println("绝对路径:"+listing3.toAbsolutePath());

相对路径:.\example2.txt
绝对路径:Y:\item\workspaces\workspace-mp\alijishi\.\example2.txt
相对路径:example3.txt
绝对路径:Y:\item\workspaces\workspace-mp\alijishi\example3.txt

在Linux系统中,还有一种符号链接。比如/usr/logs只是一个符号链接,真实的位置在/application/logs,这个位置才是日志文件真正所在的位置。如何通过符号链接找到真实位置呢?通过toRealPath方法:

Path listing4 = Paths.get("example4.txt").toRealPath(options...);

1.4 对Path的合并和比较

Path prefix = Paths.get("Y:/prefix");
Path completePath = prefix.resolve("example4");
System.out.println("完整路径:"+completePath);
boolean start = completePath.startsWith("Y:/prefix");
System.out.println("completePath是否以Y:/prefix开头:"+start);
boolean end = completePath.endsWith("example3");
System.out.println("completePath是否以example3结尾:"+end);
Path listing5 = Paths.get("Y:/prefix/example5");
Path relativizePath = completePath.relativize(listing5);
System.out.println("completePath和listing5的相对位置:"+relativizePath);

完整路径:Y:\prefix\example4
completePath是否以Y:/prefix开头:true
completePath是否以example3结尾:false
completePath和listing5的相对位置:..\example5

1.5 在目录中查找文件

Path listing6 = Paths.get("Y:/path/usecase");
try(DirectoryStream<Path> stream = Files.newDirectoryStream(listing6,"*.txt")) {
    for(Path path:stream) {
        System.out.println(path);
    }
}

Y:\path\usecase\example.txt

DirectoryStream的第一个参数是目录位置,第二个参数是模式匹配(glob模式匹配),查询所有txt文件并打印出来。 

1.6 遍历目录树

Path listing7 = Paths.get("Y:/path/usecase");
Files.walkFileTree(listing7, new PrintFileVisitor());

private static class PrintFileVisitor extends SimpleFileVisitor<Path> {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
        throws IOException
    {
        Objects.requireNonNull(file);
        Objects.requireNonNull(attrs);
        System.out.println(file);
        return FileVisitResult.CONTINUE;
    }
}

Y:\path\usecase\example.txt
Y:\path\usecase\mu1\example.txt
Y:\path\usecase\mu1\mu11\example.txt
Y:\path\usecase\mu1\mu12\example.txt
Y:\path\usecase\mu2\example.txt
Y:\path\usecase\mu3\example.txt
Y:\path\usecase\mu3\mu31\example.txt
Y:\path\usecase\mu3\mu32\example.txt
Y:\path\usecase\mu3\mu33\example.txt
Y:\path\usecase\mu3\mu33\mu331\example.txt

在java.nio中,有一个FileVisitor接口。通过这个接口,可以实现目录树的遍历。这个接口有四个方法:

  • FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) 访问上层目录
  • FileVisitResult visitFile(T file, BasicFileAttributes attrs) 访问当前目录文件
  • FileVisitResult visitFileFailed(T file, IOException exc)  访问文件失败怎么处理
  • FileVisitResult postVisitDirectory(T dir, IOException exc) 访问下层目录

java.nio提供了一个默认实现SimpleFileVisitor,这个类实现了遍历功能,如果你想在遍历的过程中加点操作,就可以直接继承SimpleFileVisitor。上面的例子就是在遍历目录的时候,看到文件就打印出来。

2 对文件的操作

上面介绍的Path接口只是对文件位置进行操作,java.nio还提供了一个Files工具类,专门操作文件。可以取代java.io中的File类。

2.1 创建和删除文件

创建文件的时候还可以指定文件属性:

Path listing8 = Paths.get("Y:/path/usecase/created.txt");
//UNIX系统
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-rw-rw-");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);
Files.createFile(listing8,attr);
Files.delete(listing8);

2.2 文件复制和移动

Path listing9 = Paths.get("Y:/path/usecase/example.txt");
Path listing10 = Paths.get("Y:/path/usecase/copy/example.txt");
Files.copy(listing9,listing10,StandardCopyOption.COPY_ATTRIBUTES,StandardCopyOption.REPLACE_EXISTING);

 移动文件的时候,有三个选项可以配置:

  • COPY_ATTRIBUTES是指复制文件的时候把文件属性也一起复制过去;
  • ATOMIC_MOVE是指确保两边操作都成功,否则回滚。在复制的时候用不着;
  • REPLACE_EXISTING是指如果目标文件存在该文件,就覆盖。

2.3 文件属性

每个文件都有它的属性,比如访问权限控制、修改时间、文件大小、文件类型、是不是目录、是不是符号链接……

文件属性分为通用属性和操作系统专有属性。通用属性在java.nio有一个BasicFileAttributes接口,通过以下代码可以获取:

BasicFileAttributes attr = Files.readAttributes(listing9, BasicFileAttributes.class);

在Files类中,对每个通用属性都提供了对应的获取方法,这里不一一探讨。

对于操作系统专有属性,java.nio允许操作系统开发者提供实现FileAttributeView接口和BasicFileAttributes接口,像PosixFileAttributes就是Unix系统专有的属性,这里不做探讨。但是设置文件属性的时候一定要注意系统的兼容性。

2.4 监控文件的变化

在java.nio中,提供了一个WatchService接口,WatchService的实例可以监控文件或目录的变化,监控到变化的时候就会返回一个事件。这在很多重要场景中使用,比如对应用的环境变量监控。

try {
    WatchService watcher = FileSystems.getDefault().newWatchService();
    Path dir = Paths.get("Y:/path/usecase");
    //检测变化
    WatchKey key = dir.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
            
    while(true) {
        key = watcher.take();
                
        for(WatchEvent<?> event:key.pollEvents()) {
            if(event.kind()==StandardWatchEventKinds.ENTRY_MODIFY) {
                System.out.println("dir changed!");
            }
        }
    }
} catch (Exception e) {
            
}

这个也是用观察者模式来实现。监控器WatchKey是个阻塞对象,可以监控多个目录/文件的多个不同事件类型。有点类似于网络IO中的Selector,也是个IO多路复用。下面是可以监控的四种事件:

  • ENTRY_CREATE 创建事件
  • ENTRY_DELETE 删除事件
  • ENTRY_MODIFY 修改事件
  • OVERFLOW 磁盘不够事件

3.总结

本文并没有把相关的类和方法全部覆盖,重点讲了Path和Files的用法,他们分别处理文件位置和文件。至于文件内容的操作超出了本文的范围。掌握了这三个部分的操作,就完整地掌握了对文件的操作。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
半臻 半臻
3年前
Python基础8——文件操作
16文件操作16.1文件操作的基本概念文件操作的步骤1.打开文件2.读、写文件3.关闭文件open函数,创建一个file对象,默认是以只读的方式打开read方法:一次性读取文件的所有内容write方法:将指定内容写入文件close方法:关闭文件file对象的属性flie.name文件的名称file.mode文件的访问模式file.closed
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
3年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Wesley13 Wesley13
3年前
oracle调用java代码
  内容主要包括:java开发,打包java程序成jar文件,上传jar文件到Oracle所在系统,Oracle导入jar文件中java类到数据库中,Oracle根据导入java类创建函数,Oracle操作函数。  用到的软件:Oracle数据库,Oracle数据库安装在Linux操作系统上;java用IDEA开发;上传jar文件用winSCP;操作Or
Wesley13 Wesley13
3年前
Java NIO 三大组件之 Channel
JavaNIO之Channel一、什么是ChannelChannel用于源节点(例如磁盘)与目的节点的连接,它可以进行读取,写入,映射和读/写文件等操作。在JavaNIO中负责缓冲区中数据的传输。Channel本省不存储数据,因此需要配合缓冲区进行传输。(个人理解其实就是相当于保存两通信地间的
Stella981 Stella981
3年前
Netty学习(3):文件操作
概述在Netty学习(2)中,我们先浅浅认识了NIO的3大核心组件,现在就让我们针对其深入学习,通过一些简单的文件操作来深入理解其中的Buffer和Channel的概念。文件写入将内存中的数据写入到文件中,如果文件不存在,那么就新建文件。//数据文件privat
Wesley13 Wesley13
3年前
Java NIO学习笔记
JavaNIO是什么JavaNIO(NewIO)是从Java1.4版本开始引入的一个新的IOAPI,可以替代标准的JavaIOAPI。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。JavaNIO与IO
Wesley13 Wesley13
3年前
JAVA NIO入门(一)
一、JAVANIO概念  JAVANIO是从JDK1.4就开始有的,之前只用过IO流,其实NIO和IO一样都是可以用来读取或者写入文件,只不过原来的IO是面向流进行操作的,而NIO是面向缓冲区进行操作二、通过一个小例子初步了解下NIO如何进行文件读写 !(https://oscimg.oschina.net/oscnet/
可莉 可莉
3年前
08. Java NIO FileChannel 文件通道
JavaNIO中的FileChannel是用于连接文件的通道。通过文件通道可以读、写文件的数据。JavaNIO的FileChannel是相对标准JavaIOAPI的可选接口。FileChannel不可以设置为非阻塞模式,他只能在阻塞模式下运行。打开文件通道(OpeningaFileChannel)