使用FileVisitor<T>接口遍历目录树,删除文件

1、接触FileVisitor接口的背景

在这疫情期间,在家办公补了些java的基础知识,上学时学的囫囵吞枣,现在补的时候才发现遗漏了好些好东西。
接了这样的一个任务,每月定时删除系统产生的大量废弃文件,数量达到很多万份,大小达几十个G(50G吧)。哦,删除文件当然是有条件的嘛,删除一个月之外的文件,不然直接使用java调用命令端口,删除存放垃圾文件的目录,让操作系统自己去递归删除就行了,我就不用费这个心劲儿了。
刚开始没注意这个量,随手就写了个递归删除。试运行的时候就感觉不对劲儿了,反正很耗时。。。
所以在各种的了解查询下,接触到了FileVisitor<T>这个接口。
首先,使用这个接口遍历文件的速度确实比之前的递归块,大概快个1/2,但是它消耗的内存却要比之前的递归要多些(至于这一点,由于个人能力限制,想着各种办法都没调出理想的内存消耗。。。)。但是整体来说,使用FileVisitor<T>遍历文件树,确实方便得多;
其次,FileVisitor<T>对于不熟悉这个接口的用法的同学是非常麻烦的,因为它代码可读性低,不像直接写的递归那个直观。相反的FileVisitor<T>的代码扩展性却比直接写的递归高很多。

2、FileVisitor介绍

2.1、基本介绍

实现FileVisitor<T>接口,重写这个接口的四个方法:preVisitDirectoryvisitFilevisitFileFailedpostVisitDirectory
这几个方法的返回值都是枚举类型
CONTINUE:继续;
SKIP_SIBLINGS:继续遍历,不过忽略当前节点的兄弟节点,直接返回上一层继续遍历
SKIP_SUBTREE:继续遍历,忽略后代目录,但继续访问子文件
TERMINATE:终止遍历

2.2、运作流程

preVisitDirectory作为遍历文件目录的一个入口,携带得有该目录的基本属性。如果preVisitDirectory在访问目录中的条目时发现是文件,而非目录,这时调用visitFile方法,来访问该文件,这个方法同样携带得有此文件的基本属性。当被访问的文件无法被正常访问时调用visitFileFailed方法。当preVisitDirectory访问的某个目录里的条目(已经访问了所有的后代)被全部访问完时,调用postVisitDirectory

2.3、文件访问者

该接口的实现被提供给Files.walkFileTree方法来访问文件树中的每个文件
Files.walkFileTree(Path start, Set options,int maxDepth,FileVisitor<? super Path> visitor )
参数介绍
Path start:起始文件,也就是起始文件地址
Set<FileVisitOption> options:配置遍历的选项,一般为符号链接
简单理解一下符号链接(软连接):
是一种特殊的文件在这个文件中,保存了另一个文件的路径,打开符号链接的时候,如不特别说明,实际上打开的是保存的路径所对应的文件。
简单的理解就像:符号链接就像是快捷方式
还有一种叫做硬连接:硬链接就像是一个同步的副本,删掉其中一个也不会影响另一个的存在。
连接软、硬连接可以参考:https://blog.csdn.net/qq_42777071/article/details/105537540
int maxDepth: 要访问的目录级别的最大数量,就是要遍历的深度,要具体遍历到第几层
FileVisitor<? super Path> visitor :为每个文件调用的文件访问者

3.0、删除文件代码

public class NioDelFile implements FileVisitor<Path> {

    /**
     * 符号链接:是一种特殊的文件
     *在这个文件中,保存了另一个文件的路径,打开符号链接的时候,
     * 如不特别说明,实际上打开的是保存的路径所对应的文件。
     * 符号链接就像是快捷方式。
     * 而硬链接就像是一个同步的副本,删掉其中一个也不会影响另一个的存在。
     */
    public static void main(String[] args){

        Path directory = Paths.get("E:\\RuiPuKeJi\\del_file(2)");
        EnumSet opts1 = EnumSet.of(FileVisitOption.FOLLOW_LINKS); // 遵循符号链接
        try {
            Runtime runtime = Runtime.getRuntime();
            long time1 = System.currentTimeMillis();
            long memory1 = runtime.freeMemory();
            Files.walkFileTree(directory, opts1, 5, new NioDelFile());
            long memory2 = runtime.freeMemory();
            long time2 = System.currentTimeMillis();
            System.out.println("---------------------------------------------------------");
            System.out.println("运行时间--->"+(time2-time1));
            System.out.println("运行内存--->"+(memory2-memory1));
            System.gc();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * preVisitDirectory:被遍历文件目录的一个入口,并且携带得有该目录的基本属性
     * @param dir
     * @param attrs
     * @return
     * @throws IOException
     */
    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        return FileVisitResult.CONTINUE;
    }

    /**
     * 访问文件时的一个方法,并会携带文件的基本属性
     * @param file    对该文件的引用
     * @param attrs  里面存放的是文件的基本属性
     * @return
     * @throws IOException
     */
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        deleteFileByFile(file,attrs);
        return FileVisitResult.CONTINUE;
    }

    /**
     * 为无法访问的文件时调用的方法
     * @param file
     * @param exc
     * @return
     * @throws IOException
     */
    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
        return FileVisitResult.CONTINUE;
    }

    /**
     * 当文件目录中的所以子文件访问结束后再调用目录,并且已经访问了所有的后代
     * @param dir
     * @param exc
     * @return
     * @throws IOException
     */
    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        return FileVisitResult.CONTINUE;
    }

    /**
     *
     * @param file 对该文件的引用
     * @param attrs 该文件的基本属性
     * @throws IOException
     */
    public void deleteFileByFile(Path file,BasicFileAttributes attrs) throws IOException {
        if (getLastTime()-attrs.lastModifiedTime().toMillis()>0){
            boolean flag = Files.deleteIfExists(file);
            System.out.println("删除成功了?"+(flag?"成功":"失败"));
        }
    }
   /**
	* 获取一个月之前的时间
	*/
    private long getLastTime() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.MONTH,-1);
        return calendar.getTimeInMillis();
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值