在Java旧版本中遍历文件系统只能通过File类通过递归来实现,但是这种方法不仅消耗资源大而且效率低。
NIO.2的Files工具类提供了两个静态工具方法walk()和walkFileTree()可用来高效并优雅地遍历文件系统。walkFileTree()功能更强,可自定义实现更多功能:遍历目录文件、拷贝目录文件、移动目录文件和删除目录文件;也可按模式匹配查询文件。
一、walk()方法原型:
Stream
方法的三个参数:
start:表示要遍历的根目录的路径。
maxDepth:表示要遍历的最大深度。如果maxDepth为0,则只遍历根目录,不遍历其子目录。如果maxDepth为正整数,则遍历根目录和所有深度不超过maxDepth的子目录。如果maxDepth为负数,则遍历根目录和所有子目录。
options:表示遍历选项。可选项包括FileVisitOption.FOLLOW_LINKS和FileVisitOption.NOFOLLOW_LINKS。
如果选择FOLLOW_LINKS选项,则遍历符号链接指向的目录;
如果选择NOFOLLOW_LINKS选项,则遍历符号链接本身。
Files.walk()方法应用实例 使用walk()方法编写的遍历文件和目录例程,简洁明了
package nio;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class WalkDemo {
public static void printFileTreeWithWalk(String rootPath) throws IOException {
Path start = Paths.get(rootPath);
int maxDepth = Integer.MAX_VALUE;
try {
Files.walk(start, maxDepth).forEach(System.out::println);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws IOException {
String rootPath = "D:/src";
printFileTreeWithWalk(rootPath);
}
}
二、walkFileTree()方法原型:
static Path Files.walkFileTree(Path start, FileVisitor<? super Path> visitor) throws IOException
参数:其中的start表示一个目录,开始遍历文件系统的起点;
其中的visitor是遍历过程中的行为控制器,它是FileVisitor 接口的实现类;
FileVisitor 接口
此接口的主要方法:
- postVisitDirectory(T dir, IOException exc) 访问目录后,这里可做收尾处理
- preVisitDirectory(T dir, BasicFileAttributes attrs) 访问目录前,这里可做初化处理
- visitFile(T file, BasicFileAttributes attrs) 访问文件或目录时进行的操作
- visitFileFailed(T file, IOException exc) 在试图访问文件或目录,发生错误时善后处理。例如:没有权限打开目录;
上面的方法用到了两个类介绍
1,FileVisitResult 类: 它是一个枚举类。枚举类定义了在访问某一个目录文件时,有访问前、访问后、访问中和访问失败这四个节点的操作策略。
FileVisitor 接口的内部枚举类FileVisitResult 类定义的(访问策略)常量:
- CONTINUE 继续访问一下一个文件
- SKIP_SIBLINGS 继续访问,但是不再访问这个目录下的任何项了
- SKIP_SUBTREE 继续访问,但是不再访问这个文件的兄弟文件(和该文件在同一目录下的文件)
- TERMINATE 终止访问
当有任何异常抛出时,就会终止访问,而这个异常就会从walkFileTree方法中抛出。
2,BasicFileAttributes类: 基本文件属性类BasicFileAttributes类, 描述了文件的通用属性,它提供了很多方法来获得当前访问文件的各种属性,比如文件的大小,上次修改时间,是否是目录等:
FileTime creationTime() 创建时间
FileTime lastAccessTime() 最后一次访问时间
FileTime lastModifiedTime() 最后一次编辑时间
boolean isRegularFile() 是常规文件
boolean isDirectory() 是目录文件
boolean isSymoblicLink() 是符号链接
boolean isOther() 上面三种类型都不是
long size() 文件长度
Object fileKey() 返回文件唯一标识
通过读取文件属性对象可以获取文件的各种属性信息(直接使用Files类的一些方法也可以获取或设置部分属性信息)。
BasicFileAttributes接口描述了文件的通用属性(FileTime类型的创建/最后一次访问/最后一次修改时间,常规文件/目录/符号链接,文件大小,文件主键标识),通用属性获取方式为: BasicFileAttributes attributes=Files.readAttributes(path, BasicFileAttributes.class);
SimpleFileVisitor是FileVisitor 接口的实现类
为了简化FileVisitor繁杂操作的编写工作量,Java NIO库还提供了一个实现了FileVisitor接口的实现类SimpleFileVisitor。SimpleFileVisitor提供了文件树遍历的简单实现,SimpleFileVisitor就是判断一下参数是不是空,为空则抛出异常,不为空就继续遍历。
可以通过继承 SimpleFileVisitor 并重写其中的方法,自定义遍历文件和目录时的操作。
实际使用时,常用继承自SimpleFileVisitor,因为只需要实现你感兴趣的方法即可,无需4个方法全部都覆盖实现。
Files.walkFileTree()方法应用实例
(一)、使用walkFileTree()方法编写的遍历文件和目录例程,使用FileVisitor接口的匿名类。
package nio;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
public class WalkFileTreeDemo {
public static void printFileTree(String rootPath) throws IOException {
Path path = Paths.get(rootPath);
if (Files.notExists(path)) {
String msg = "目录不存在:"+rootPath;
System.out.println(msg);
return;
}
Files.walkFileTree(path, new FileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes arg1) throws IOException {
System.out.println(path.toString());
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path arg0, IOException arg1) throws IOException {
System.out.println("访问目录"+ arg0.toString()+"后,可做收尾处理" );
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path arg0, BasicFileAttributes arg1) throws IOException {
System.out.println("访问目录" + arg0.toString()+"前,可做初始化处理");
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path arg0, IOException arg1) throws IOException {
return FileVisitResult.CONTINUE;
}
});
}
public static void main(String[] args) throws Exception {
String rootPath = "D:/src";
printFileTree(rootPath);
}
}
上面两个例程实现相同的目录遍历功能,都可遍历子目录信息,两种测试结果如下:(为了便于比较,放同一图上)
二、使用walkFileTree()方法实现文件检索功能的例程
例程使用了正则表达式作为匹配参数,使程序更加灵活。
package nio;
/***
* @author QiuGen
* @description WalkFileTree使用实例,
* 实现功能:正则表达式实现文件访问控制器
* 1,在目录树中根据正则表达式查找文件;
* 2,递归删除目录树中的文件夹(代码在下一节)
* @date 2024/10/18
* ***/
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.regex.Pattern;
public class WalkFileTreeTest {
public static void queryFileTree(String path,String regex) throws IOException {
Path start = Paths.get(path);
try {
Files.walkFileTree(start, new QueryFileVisitor(regex));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
String path = "D:/src";
String regex = ".*\\.mp3";// "大约.*\\.mp3";
WalkFileTreeTest.queryFileTree(path,regex);
}
}
class QueryFileVisitor extends SimpleFileVisitor<Path> {
private Pattern pattern;
public QueryFileVisitor(String regex) {
pattern = Pattern.compile(regex);
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String filePath = file.toAbsolutePath().toString();
String name = file.getFileName().toString();
//if(filePath.endsWith("test1.txt")){
if(pattern.matcher(name).matches()){
System.out.println("找到路径:" + filePath);
return FileVisitResult.CONTINUE;
}
return super.visitFile(file, attrs);
}
}
文件检索功能的例程的测试效果图:
三、使用walkFileTree递归删除目录的例程
walkFileTree还可以用来递归删除目录及其目录中所有文件,Files.delete()只能删除空目录或者文件,当目中包含文件的时候无法直接删除,只能删除目录下所有文件后才能删除目录,walkFileTree可以在isitFile()方法中删除文件,然后在postVisitDirectory()删除目录实现递归删除目录。
递归删除目录树中的文件夹例程:
/***2,递归删除目录树中的文件夹的功能***/
public static void delFilePath(String path) {
Path start = Paths.get(path);
try {
Files.walkFileTree(start, new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("删除文件:" + file);
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
System.out.println("删除目录:" + dir);
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
调用方式:
String path = "D:/src/tmp";
WalkFileTreeTest.delFilePath(path);
例程测试效果图:
四、使用walkFileTree()方法编写的目录拷贝例程: 复制目录包括子目录中内容
package nio;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
public class DirCopyDemo { // 复制目录:复制目录包括子目录中内容
public static void main(String[] args) throws IOException {
Path sourceDirPath = Paths.get("D:/src");
Path targetDirPath = Paths.get("D:/temp/test");
try {
Files.walkFileTree(sourceDirPath, new CopyFileVisitor(sourceDirPath, targetDirPath));
System.out.println("目录复制成功");
} catch (IOException e) {
System.out.println("目录复制失败:" + e.getMessage());
}
}
}
/***定制CopyFileVisitor***/
class CopyFileVisitor extends SimpleFileVisitor<Path> {
private final Path sourceDir;
private final Path targetDir;
public CopyFileVisitor(Path sourceDir, Path targetDir) {
this.sourceDir = sourceDir;
this.targetDir = targetDir;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path targetPath = targetDir.resolve(sourceDir.relativize(dir));
try {
Files.copy(dir, targetPath);
} catch (FileAlreadyExistsException e) {
if (!Files.isDirectory(targetPath)) {
throw e;
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Path targetPath = targetDir.resolve(sourceDir.relativize(file));
Files.copy(file, targetPath, StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
}
参考文献资料:
- 【脚本之家】Java Files和Paths的使用demo详解
- 【CSDN】Files类的walkFileTree方法