文档章节

Java NIO之文件处理

士别三日
 士别三日
发布于 06/24 12:19
字数 2099
阅读 10
收藏 0
点赞 0
评论 0

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

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

其中,对文件内容的操作在 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<T>接口。通过这个接口,可以实现目录树的遍历。这个接口有四个方法:

  • 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<T>,这个类实现了遍历功能,如果你想在遍历的过程中加点操作,就可以直接继承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的用法,他们分别处理文件位置和文件。至于文件内容的操作超出了本文的范围。掌握了这三个部分的操作,就完整地掌握了对文件的操作。 

© 著作权归作者所有

共有 人打赏支持
士别三日

士别三日

粉丝 35
博文 30
码字总数 43081
作品 0
深圳
程序员
Java NIO原理 图文分析及代码实现

Java NIO原理图文分析及代码实现 前言: 最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术...

囚兔
2015/04/29
0
0
Java NIO原理图文分析及代码实现

前言: 最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。可以参考:http://baik...

SunnyWu
2014/11/05
0
1
Java NIO系列教程(一) Java NIO 概述

Java NIO 由以下几个核心部分组成: Channels Buffers Selectors 虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的API。其它组件,如Pipe和...

只想一个人静一静
2014/02/22
0
0
Java NIO AsynchronousFileChannel

原文链接 , 原文作者:Jakob Jenkov, 翻译:Neil Hao 在Java 7,AsynchronousFileChannel 被添加到了Java NIO中。使用AsynchronousFileChannel可以实现异步地读取和写入文件数据。 创建一个A...

Neil_Hao
01/20
0
0
Java NIO系列教程(十二) Java NIO与IO

原文地址:http://tutorials.jenkov.com/java-nio/nio-vs-io.html 作者:Jakob Jenkov 当学习了Java NIO和IO的API后,一个问题马上涌入脑海: 我应该何时使用IO,何时使用NIO呢?在本文中,我...

_夏天的风_
2013/07/11
0
0
tomcat-tomcat bio nio apr 模式性能测试与个人看法

11.11活动当天,服务器负载过大,导致部分页面出现了不可访问的状态、那后来主管就要求调优了,下面是tomcat bio、nio、apr模式以及后来自己测试的一些性能结果。 原理方面的资料都是从网上找...

抢地主
2017/11/04
0
0
java NIO原理及通信模型

Java NIO是在jdk1.4开始使用的,它既可以说成“新IO”,也可以说成非阻塞式I/O。下面是java NIO的工作原理: 由一个专门的线程来处理所有的IO事件,并负责分发。 事件驱动机制:事件到的时候...

柳哥
2015/02/15
0
4
Tomcat并发数优化的方法总结

web应用的并发提升,除了负载均衡。在小企业中也可以通过一些软件的上的设置来进行一些优化。下面是一些在服务器上修改tomcat参数的优化方法,非常简单实用!(这些方法通过网络整理的) 1,...

翊骷
2014/08/04
0
0
十二, Java NIO与IO

当学习了Java NIO和IO的API后,一个问题马上涌入脑海: 我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异、它们的使用场景,以及它们如何影响您的代码设计...

莫问viva
2014/03/07
0
0
Java NIO原理图文分析及代码实现

Java IO 在Client/Server模型中,Server往往需要同时处理大量来自Client的访问请求,因此Server端需采用支持高并发访问的架构。一种简单而又直接的解决方案是“one-thread-per-connection”。...

只想一个人静一静
2014/02/22
0
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大

华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大!华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大! 在华为最新发布的nova 3手机上,抖音通过华为himedia SDK集成了60fps、超级...

华为终端开放实验室
13分钟前
0
0
多 SSH Key 实现同一台服务器部署多 Git 仓库

本文以以下需求为背景,介绍详细的做法: 需在同一台服务器同时部署两个不同的 Github 仓库(对 Bitbucket 等 git 服务同样适用) root 用户可在远程登录 SSH 后附上预期的 SSH Key 进行 gi...

yeahlife
15分钟前
0
0
003. es6数值的扩展

一、普通扩展 Number 方法,将字符串、数值转为十进制 : Number('0b111') Number.isFinite() 用来检查一个数值是否为有限的:Number.isFinite(15) Number.isNan() 用来检查一个值是否为NaN N...

秋季长青
29分钟前
0
0
rabbitmq学习(二)

基本队列:Producer直接发送信息到Queue中,Consumer接收Queue发送过来的信息 简而言之,一个生产者发送信息,一个消费者接收信息。 获取连接工具类: package com.example.demo.utils;i...

人觉非常君
34分钟前
0
0
C语言数组和指针的语法糖

对于C语言,我可以这样秀:比如当创建一个数组arr[n]之后,一般我们去遍历数组的时候是for (int i = 0; i < n; i++) { a[i]; }但是我知道下表访问符[]是个语法糖,也就是说a[i]在编译器看来是...

ustbgaofan
37分钟前
0
0
Call to undefined function bcmath()的解决方法

乐意黎的ECS主机环境,Centos7.2 + PHP7 由于使用了bcdiv()函数,运行时总在抛错。 Fatal error: Call to undefined function bcmath() in /usr/loca/apache/htdocs/... on line 4 一查得知:......

dragon_tech
43分钟前
0
0
css优先级

..

architect刘源源
48分钟前
0
0
【转】Twitter的分布式自增ID算法snowflake

结构 snowflake的结构如下(每部分用-分开): 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以...

talen
52分钟前
0
0
hive支持行级修改

Hive从0.14版本开始支持事务和行级更新,但缺省是不支持的,需要一些附加的配置。要想支持行级insert、update、delete,需要配置Hive支持事务。 一、Hive具有ACID语义事务的使用场景 1. 流式...

hblt-j
57分钟前
0
0
Python---scrapy框架的学习

scrapy框架的学习 先熟悉下scrapy项目结构: scrapyTest/scrapyTest/ _init_.py用来初始化项目信息 scrapyTest/scrapyTest/ items.py文件为爬虫项目的数据容器文件,主要用来定义我们的数据....

android-key
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部