Java监听文件夹变化&遍历三层文件夹找文件

前言

        这段时间在写一个遍历三层文件夹找文件的四级分类,在这个过程中遇到了新学了些东西,也遇到了些问题,于此记录。

一、需求

       这个需求是这样的,从一个多级文件夹中获取文件夹名称和文件名称,并将其封装成数组,格式的话是elementui中的el-cascader级联的options绑定的数据源格式,为了增强通用性,还需要在之后添加、删除、修改文件后能够同步获取,(这里使用的是监听)。

二、思路

        在项目启动时,加载获取文件夹和文件,并将其转换成json存储在redis中,每次访问直接从redis中获取即可,监听到文件夹和文件的增删改、那么就重新获取一次,再存储到redis中,由于存储的是名称,不需要担心内存问题,因此直接永久存储即可

三、实现

0.监听文件变化基本流程:

  • 自定义文件监听类继承FileAlterationListenerAdaptor实现对文件与目录的创建、修改、删除时间的处理;
  • 自定义文件监控类,通过指定目录创建一个观察者FileAlterationObserver;
  • 向监视器添加文件系统观察器,并添加文件监听器;
  • 调用执行(启动自加载)

1.导入依赖:不同的版本支持不同的JDK,2.7需要JDK 8及以上版本

  <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.7</version>
        </dependency>

2.配置FileMonitorConfig :封装一个文件监控的工具类,核心就是创建一个观察者FileAlterationObserver,将文件路径Path和监听器FileAlterationListener进行封装,然后交给FileAlterationMonitor

@Configuration
public class FileMonitorConfig {

    private FileAlterationMonitor monitor = new FileAlterationMonitor();

    @Autowired
    RedisTemplate redisTemplate;

    //监听文件路径
    @Value("${spring.file.path}")
    String path;

    //监听时间间隔
    @Value("${spring.file.interval}")
    long interval;


    /**
     * 给文件添加监听
     * listener 文件监听器
     */
    @Bean
    public void monitor() {
        FileAlterationListener listener = new FileListener(redisTemplate);
        FileAlterationObserver observer = new FileAlterationObserver(new File(path));
        monitor.addObserver(observer);
        observer.addListener(listener);
    }

    public void stop() throws Exception {
        monitor.stop();
    }
    @Bean
    public void start() throws Exception {

        //初始化
        FileUtil.path = path;
        String data = FileUtil.getAllFilesToJson();
        redisTemplate.opsForValue().set("file", data);

        monitor.start();
    }
}

3.配置FileListener:创建文件监听器。根据需要在不同的方法内实现对应的业务逻辑处理。

public class FileListener extends FileAlterationListenerAdaptor {
    private RedisTemplate redisTemplate;

    public FileListener() {}

    public FileListener(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void onStart(FileAlterationObserver observer) {
        //轮询开始
        super.onStart(observer);
//        System.out.println("onStart");
    }

    @Override
    public void onDirectoryCreate(File directory) {
//        System.out.println("新建:" + directory.getAbsolutePath());
        String data = FileUtil.getAllFilesToJson();
        redisTemplate.opsForValue().set("file", data);
        //待测试
        //新增文件夹  修改文件夹--新增文件夹
    }

    @Override
    public void onDirectoryChange(File directory) {
//        System.out.println("修改:" + directory.getAbsolutePath());
        //测试 文件夹下的文件在增删改后,是否会调用此方法
        //新增文件  会先触发修改文件夹再触发新增文件
        //修改文件  会先触发修改文件夹再触发新增和删除文件
        //删除文件  会先触发修改文件夹再触发删除文件
        //修改文件夹     修改文件夹 -- 新增文件夹 ---删除文件夹
        String data = FileUtil.getAllFilesToJson();
        redisTemplate.opsForValue().set("file", data);
    }

    @Override
    public void onDirectoryDelete(File directory) {
//        System.out.println("删除:" + directory.getAbsolutePath());
        String data = FileUtil.getAllFilesToJson();
        redisTemplate.opsForValue().set("file", data);
        //删除文件夹     修改文件夹--删除文件夹
    }

    @Override
    public void onFileCreate(File file) {
        String compressedPath = file.getAbsolutePath();
//        System.out.println("新建:" + compressedPath);
        if (file.canRead()) {
            // TODO 读取或重新加载文件内容
//            System.out.println("文件变更,进行处理");
            String data = FileUtil.getAllFilesToJson();
            if (data != null) {
//                redisTemplate.delete("file");
//                System.out.println(redisTemplate);
//                redisTemplate.opsForValue().get("file");
                redisTemplate.opsForValue().set("file", data);
            }
        }
    }

    @Override
    public void onFileChange(File file) {
        String compressedPath = file.getAbsolutePath();
//        System.out.println("修改:" + compressedPath);
        String data = FileUtil.getAllFilesToJson();
        redisTemplate.opsForValue().set("file", data);
    }

    @Override
    public void onFileDelete(File file) {
//        System.out.println("删除:" + file.getAbsolutePath());
        String data = FileUtil.getAllFilesToJson();
        redisTemplate.opsForValue().set("file", data);
    }

    @Override
    public void onStop(FileAlterationObserver observer) {

        super.onStop(observer);

//        System.out.println("onStop");
    }
}

4.编写核心代码

方法一:普通for循环

public static String getAllFilesToJson1() {
        int i = 0;
        File file = new File(path);             //获取其file对象
        File[] fs = file.listFiles();           //遍历path下的文件和目录,放在File数组中
        if (fs == null) {
//            System.out.println("null");
            return null;
        }
        FileInf[] data = new FileInf[fs.length];
        for (File f : fs) {//一级分类
            if (f.isDirectory()) {
                String[] str = f.getAbsolutePath().split("\\\\");
                FileInf fileInfo = new FileInf();
                fileInfo.setValue(str[str.length - 1]);
                fileInfo.setLabel(str[str.length - 1]);

                File[] files = f.listFiles();
                FileInf[] fileInfos1 = new FileInf[files.length];//用于存放二级分类

                //二级分类
                int j = 0;
                for (File f1 : files) {
                    if (f1.isDirectory()) {

                        String[] str1 = f1.getAbsolutePath().split("\\\\");
                        FileInf fileInfo1 = new FileInf();
                        fileInfo1.setValue(str1[str1.length - 1]);
                        fileInfo1.setLabel(str1[str1.length - 1]);

                        File[] files1 = f1.listFiles();
                        FileInf[] fileInfos2 = new FileInf[files1.length];//用于存放三级分类

                        //三级分类
                        int k = 0;
                        for (File f2 : files1) {
                            if (f2.isDirectory()) {
                                String[] str2 = f2.getAbsolutePath().split("\\\\");
                                FileInf fileInfo2 = new FileInf();
                                fileInfo2.setValue(str2[str2.length - 1]);
                                fileInfo2.setLabel(str2[str2.length - 1]);

//                                fileInfo1.setChildren(fileInfos2);
                                fileInfos2[k++] = fileInfo2;
                                File[] files2 = f2.listFiles();//四级:获取图片名称即可,无children
                                FileInf[] fileInfos3 = new FileInf[files2.length];//用于存放四级图片
                                int l = 0;
                                for (File f3 : files2) {
                                    if (f3.isFile()) {
                                        if (f3.getName().equals("Thumbs.db")) {//排除Thumbs.db文件
                                            FileInf[] tempArr = new FileInf[fileInfos3.length - 1];
                                            System.arraycopy(fileInfos3, 0, tempArr, 0, fileInfos3.length - 1);
                                            fileInfos3 = tempArr;
                                            continue;
                                        }
                                        String[] str3 = f3.getAbsolutePath().split("\\\\");
                                        FileInf fileInfo3 = new FileInf();
                                        fileInfo3.setValue(str3[str3.length - 1]);
                                        fileInfo3.setLabel(str3[str3.length - 1]);
                                        fileInfos3[l++] = fileInfo3;
                                    }
                                }
                                fileInfo2.setChildren(fileInfos3);
                            }
                        }
                        fileInfo1.setChildren(fileInfos2);
                        fileInfos1[j++] = fileInfo1;
                    }
                }

                fileInfo.setChildren(fileInfos1);
                data[i++] = fileInfo;
            }
        }

        Gson gson = new Gson();
        String sData = gson.toJson(data);
        return sData;

    }

 方法二:递归

        之后,又使用递归的方式实现了一遍,在使用递归的方式中原来的递归方法中是没有返回值的,但是因为我在排除**Thumsb.db文件(此文件是数据库文件,用于缓存Windows中图片文件的缩略图。当用户打开包含多张图片的文件夹后,系统会遍历整个目录并将该目录中的图片缩略图逐个缓存到Thumbs.db中)的时候,将data数组形参的引用地址改变了,通过"="改变,这种改变无法同步到传递之前的data数组,因此,我通过添加返回值的方式来解决这一问题。代码如下

  /**
     * 使用递归的方式遍历文件夹及文件并将其转换成json格式
     * @return
     */
    public static String getAllFilesToJson() {
        int i = 0;
//        String path = "D:\\mes系统文件";        //要遍历的路径
        File file = new File(path);        //获取其file对象
//        if(file.exists())return;
        File[] fs = file.listFiles();    //遍历path下的文件和目录,放在File数组中
        if (fs == null) {
            return null;
        }
        FileInf[] data = new FileInf[fs.length];
        //data 地址的拷贝,通过数据本身的方法修改内容,data的值同步修改
        dg(file, data);
        Gson gson = new Gson();
        String sData = gson.toJson(data);
        System.out.println("data: " + sData);
        return sData;
    }

    public static FileInf[] dg( File file,FileInf[] data) {
        //1.递归退出条件
        if (!file.isDirectory()) {
            return data;
        }
        //2.处理当前层逻辑
        File[] fs = file.listFiles();

        int x = 0;
        for (File f : fs) {
            if (f.isDirectory()) {
                String[] str = f.getAbsolutePath().split("\\\\");
                FileInf fileInfo = new FileInf();
                fileInfo.setValue(str[str.length-1]);
                fileInfo.setLabel(str[str.length-1]);
                File[] files = f.listFiles();
//                FileInf[] fileInfos1 = new FileInf[files.length];//存储子级的children属性

                //3.递归调用
                FileInf[] fileInfos1 = dg(f, new FileInf[files.length]);
                System.out.println("len: " + fileInfos1.length);
                fileInfo.setChildren(fileInfos1);
                data[x++] = fileInfo;//存储本级文件夹
            }
            else {
                if (f.getName().equals("Thumbs.db")) {//排除Thumbs.db文件
                    //这里改变data的长度,没有影响到fileInfos1,所以将dg方法原来的无返回值改为返回FileInf[],通过一级一级返回修改fileInfos1的地址
                    FileInf[] tempArr = new FileInf[data.length - 1];
                    System.arraycopy(data, 0, tempArr, 0, data.length - 1);
                    //通过=赋值,修改了形参data的地址,但是没有修改传之前data的地址,所以递归方法加入返回值,在最下面返回data
                    data = tempArr;
                    continue;
                }
                String[] str = f.getAbsolutePath().split("\\\\");
                FileInf fileInfo = new FileInf();
                fileInfo.setValue(str[str.length - 1]);
                fileInfo.setLabel(str[str.length - 1]);
                data[x++] = fileInfo;//存储四级图片
            }
        }
        return data;
    }

四、总结

在编写递归时,对方法传参有了更深的了解,并写了一个测试类,对我的几种猜测进行了验证,得出了一下结论

方法传参

1.基本类型,传值的拷贝,改变的是形参的值,不改变传之前参数的值

2.引用类型,传地址的拷贝

通过=赋值改变的是形参的地址,不改变传之前参数的地址

通过set等(num[x] = x)对象本身的方法改变,改变的是内容

@SpringBootTest
public class ParamTest {
    @Test
    public void test() {
        //基本类型传参
        int a = 1;
        test2(a);
        System.out.println("a: "+a);//输出:1
        //引用类型传参,使用赋值=改变地址
        int[] arr = {1, 2, 3};
        test3(arr);
        System.out.println(Arrays.toString(arr));//输出:[1,2,3]
        //引用类型传参,使用对象内部方法改变地址
        test4(arr);
        System.out.println(Arrays.toString(arr));//输出:[3,3,3]
    }
    public void test2(int a) {
        a = 2;
    }
    public void test3(int[] arr) {
        int[] num = {1,2};
        arr = num;
    }
    public void test4(int[] arr) {
        arr[0] = 3;
        arr[1] = 3;
    }
}

监听FTP文件夹变化,你需要使用FTPClient类来连接到FTP服务器。然后,你可以使用FTPClient类提供的一些方法来监视FTP文件夹变化,例如: 1. 使用FTPClient.changeWorkingDirectory()方法进入要监视的文件夹。 2. 使用FTPClient.listFiles()方法获取当前文件夹中的所有文件和子文件夹。 3. 使用FTPClient.retrieveFileStream()方法获取指定文件的输入流,并使用BufferedReader读取输入流中的内容。 你可以将以上步骤封装在一个循环中,以便每隔一段时间重复执行。如果发现有新的文件文件夹被添加到FTP文件夹中,就可以执行相应的操作。 以下是一个示例代码片段,可以用来监听FTP文件夹变化: ``` FTPClient client = new FTPClient(); client.connect("ftp.example.com"); client.login("username", "password"); while (true) { client.changeWorkingDirectory("/path/to/ftp/folder"); FTPFile[] files = client.listFiles(); for (FTPFile file : files) { if (file.isFile()) { InputStream inputStream = client.retrieveFileStream(file.getName()); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); // 处理文件内容 reader.close(); inputStream.close(); } else if (file.isDirectory()) { // 处理子文件夹 } } Thread.sleep(5000); // 暂停5秒钟 } client.logout(); client.disconnect(); ``` 请注意,上述代码仅提供了基本的框架。你需要根据自己的需求进行修改和完善。此外,还需要考虑到连接失败、文件读取异常等情况的处理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值