Java中SimpleFileVisitor的应用:在指定文件路径下搜索特定格式的图片

需求:在指定文件路径中,有多个文件夹,查询每个文件下是否有规定格式的图片,并将文件夹名称打印出来。

一、 什么是 SimpleFileVisitor?

SimpleFileVisitor 是 Java NIO库中的一个类,实现了 FileVisitor 接口,提供了文件树遍历的简单实现。可以通过继承 SimpleFileVisitor 并重写其中的方法,自定义遍历文件和目录时的操作。SimpleFileVisitor 中的几个关键方法:

  • preVisitDirectory: 在访问目录之前调用,可以用于初始化状态。
  • visitFile: 在访问文件时调用,是文件处理的核心方法。
  • visitFileFailed: 当文件访问失败时调用,例如权限问题。
  • postVisitDirectory: 在访问完目录及其内容之后调用,用于收尾工作。

1. preVisitDirectory(T dir, BasicFileAttributes attrs)

  • 作用:在访问目录之前调用。
  • 参数
    • dir: 当前目录的路径。
    • attrs: 该目录的基本文件属性。
  • 返回值:返回 FileVisitResult,决定如何继续遍历:
    • CONTINUE: 继续遍历。
    • SKIP_SUBTREE: 跳过当前目录的子项,但继续遍历其他部分。
    • SKIP_SIBLINGS: 跳过当前目录的兄弟节点。
    • TERMINATE: 终止遍历。
  • 默认实现:返回 CONTINUE

用途示例:可以用来在进入目录前做一些初始化工作,比如重置标志变量。

2. visitFile(T file, BasicFileAttributes attrs)

  • 作用:在访问文件时调用。
  • 参数
    • file: 当前文件的路径。
    • attrs: 该文件的基本文件属性。
  • 返回值:返回 FileVisitResult,决定如何继续遍历。
  • 默认实现:返回 CONTINUE

用途示例:可以用来检查文件的类型、读取文件内容或统计文件数量等。

3. visitFileFailed(T file, IOException exc)

  • 作用:当文件访问失败时调用,比如文件权限不足或文件不存在。
  • 参数
    • file: 访问失败的文件路径。
    • exc: 抛出的异常信息。
  • 返回值:返回 FileVisitResult,决定如何继续遍历。
  • 默认实现:抛出异常,从而终止遍历。

用途示例:可以用来记录日志或者处理错误,防止遍历因某个错误而中断。

4. FileVisitResult postVisitDirectory(T dir, IOException exc)

  • 作用:在访问完目录及其内容之后调用。
  • 参数
    • dir: 当前访问的目录路径。
    • exc: 如果访问过程中出现异常,则传入异常;否则为 null
  • 返回值:返回 FileVisitResult,决定如何继续遍历。
  • 默认实现:返回 CONTINUE

用途示例:可以用来执行在访问目录之后的操作,比如统计目录中的文件数量或者清理资源。

二、代码示例

1.代码实现

import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;

/**
 * @author shijiangyong
 * @date 2024/9/2 11:14
 * @description:
 */
public class SimpleVisitController {
        public static void main(String[] args) {
            // 指定需要遍历的文件夹路径
            Path dir = Paths.get("E:/software/test");

            try {
                // 调用方法,获取没有图片的文件夹名
                List<String> noImageFiles = findFilesWithoutImages(dir);
                noImageFiles.forEach(System.out::println);
            } catch (IOException e) {
                System.err.println("Error accessing files: " + e.getMessage());
            }
        }

        // 查找没有图片的文件夹的方法
        public static List<String> findFilesWithoutImages(Path dir) throws IOException {
            List<String> noImageFiles = new ArrayList<>();
            Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
                // 标记文件夹中是否有图片
                private boolean hasImage = false;
                // 排除搜索路径
                private Path topDir;
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    if (topDir == null) {
                        // 设置顶级目录
                        topDir = dir;
                    }
                    hasImage = false;
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    if (isImageFile(file)) {
                        // 如果发现图片文件,将标记设置为true
                        hasImage = true;
                    }
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    // 捕获并处理文件权限问题
                    if (exc instanceof AccessDeniedException) {
                        System.err.println("Access denied: " + file.toString());
                        // 继续遍历其他文件
                        return FileVisitResult.CONTINUE;
                    } else {
                        // 对于其他异常,可以选择继续或终止遍历
                        // 或者记录日志并返回 CONTINUE
                        throw exc;
                    }
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    if (!hasImage && !dir.equals(topDir)) {
                        noImageFiles.add(dir.toString());
                    }
                    return FileVisitResult.CONTINUE;
                }

                // 判断文件是否包含指定图片格式
                private boolean isImageFile(Path file) {
                    String fileName = file.toString().toLowerCase();
                    return fileName.endsWith(".jpg") || fileName.endsWith(".png") ||
                            fileName.endsWith(".gif") || fileName.endsWith(".bmp") ||
                            fileName.endsWith(".jpeg");
                }
            });
            return noImageFiles;
        }
}

我在指定目录下新建了三个文件夹,没有图片。
image.png
运行结果:

E:\software\test\example
E:\software\test\simple
E:\software\test\visit

在simple文件下添加一张图片运行结果:

E:\software\test\example
E:\software\test\visit

2.拓展用法

这里显示的是查找,也可以批量命名或删除文件。

2.1.重命名

对指定格式的文件重命名,调整visitFile方法的代码:

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
    if (isImageFile(file)) {
        // 如果发现图片文件,将标记设置为true
        hasImage = true;
    }
    // 检查是否是 .txt 文件
    if (file.toString().toLowerCase().endsWith(".txt")) {
        // 构建新的文件名
        Path renamedFile = file.resolveSibling("processed_" + file.getFileName());
        // 执行重命名操作
        Files.move(file, renamedFile);
        System.out.println("Renamed: " + file + " to " + renamedFile);
    }
    return FileVisitResult.CONTINUE;
}

运行结果:

Renamed: E:\software\test\visit\test.txt to E:\software\test\visit\processed_test.txt

2.2 删除指定文件

比如示例代码逻辑调整为没有找到指定格式图片,且文件夹是空的,则删除文件夹,调整postVisitDirectory代码:

 @Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { 
    if (!hasImage && !dir.equals(topDir)) {
        try {
            Files.delete(dir);
            System.out.println("Deleted folder: " + dir);
        } catch (DirectoryNotEmptyException e) {
            // 捕获并处理文件夹不为空的情况
            System.err.println("Failed to delete folder (not empty): " + dir);
        } catch (IOException e) {
            // 处理其他可能的IO异常
            System.err.println("Failed to delete folder: " + dir + " due to " + e.getMessage());
        }
    }
    return FileVisitResult.CONTINUE;
}

运行结果(visit有个txt文件,simple中有一张图片,example啥也没有被删除了):
image.png
若是非空文件夹也要删除,继续调整代码:

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
    if (!hasImage && !dir.equals(topDir)) {
        try {
            Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Files.delete(file);
                    System.out.println("Deleted file: " + file);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    Files.delete(dir);
                    System.out.println("Deleted directory: " + dir);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            System.err.println("Failed to delete directory tree: " + dir + " due to " + e.getMessage());
        }
    }
    return FileVisitResult.CONTINUE;
}

运行结果:
image.png
在处理大量文件或深层次文件结构时,单线程遍历可能效率较低可以使用 Java 8 的并行流(Parallel Streams)或者 ForkJoinPool来提高处理速度。
删除操作是不可逆的,确保执行前备份重要数据或确认文件夹内容可以安全删除。


人生而自由,却无往不在枷锁之中。 – 卢梭

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值