Java实现校验两个目录下的文件变更记录(一)

一个小工具,用来比较两个文件目录下的文件是否有增加、删除和修改。

逻辑并不复杂,先计算文件的MD5码,然后循环遍历所有文件,依次比较即可。

主要目的是想写一个多线程版本的,复习一下多线程方面的知识。

1.新建一个maven项目,引入maven依赖

	<!--做文件校验用的依赖-->
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.11</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.5</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.5</version>
    </dependency>

2.单线程版本的校验工具

  1. MD5生成
    private static String getMD5(File file) {
        MessageDigest MD5 = null;
        try {
            MD5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException ne) {
            ne.printStackTrace();
        }
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(file);
            byte[] buffer = new byte[8192];
            int length;
            while ((length = fileInputStream.read(buffer)) != -1) {
                MD5.update(buffer, 0, length);
            }

            return new String(Hex.encodeHex(MD5.digest()));
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (fileInputStream != null)
                    fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  1. 文件校验方法
   public void checkFile(String oldPath, String newPath,String outPath) throws IOException {

        long startTime = System.currentTimeMillis();  //获取开始时间

        //三个set,分别放置更改文件、新增文件、删除文件
        Set<String> changedFilesSet = new HashSet<>();
        Set<String> newFilesSet = new HashSet<>();
        Set<String> deletedFilesSet = new HashSet<>();

        // 遍历新文件目录
        Iterator iterator = FileUtils.iterateFiles(new File(newPath), null, true);
        // 遍历旧文件目录
        Iterator iterator1 = FileUtils.iterateFiles(new File(oldPath), null, true);

        try {
            // 遍历比较新旧目录
            // 1. 如果文件不存在,则说明是新增的文件,放到newFilesSet
            // 2. 如果MD5值不一样,则文件被修改,放到changedFilesSet
            while (iterator.hasNext()) {
                // 新文件路径
                String nPathStr = iterator.next().toString();
                File newFile = new File(nPathStr);
                // 旧文件路径
                File oldFile = new File(nPathStr.replace(newPath, oldPath));
                // 判断文件是否存在
                if (!oldFile.exists()) {
                    newFilesSet.add(newFile.toString());
                } else {
                    // MD5校验
                    // 如果不相同,则文件被修改,放到changedFilesSet
                    String newMD5 = getMD5(newFile);
                    String oldMD5 = getMD5(oldFile);
                    if (!StringUtils.equals( newMD5, oldMD5 )) {
                        changedFilesSet.add(newFile.toString());
                    }
                }
            }

            // 遍历旧的文件目录,用于查找被删除的文件
            while (iterator1.hasNext()) {
                // 旧文件路径
                String oPathStr = iterator1.next().toString();
                // 新文件路径
                File newFile = new File(oPathStr.replace(oldPath, newPath));
                if (!newFile.exists()) {
                    deletedFilesSet.add(oPathStr);
                }
            }
        } catch (Exception e) {
            System.err.println("发生异常!");
        }
        
        //创建文件
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
        String fileName = outPath + "\\" + df.format(new Date()) + ".txt";
        //写入txt文件
        File file = new File(fileName);
        boolean newFile = file.createNewFile();

        try {

            PrintStream ps = new PrintStream(new FileOutputStream(file));
            ps.append("旧文件目录:").append(oldPath).append("\n");
            ps.append("新文件目录:").append(newPath).append("\n");
            ps.append("新增文件:\n");
            Iterator<String> newFilesPath = newFilesSet.iterator();
            while (newFilesPath.hasNext()) {
                ps.append("  ").append(String.valueOf(newFilesPath.next())).append("\n");
            }
            ps.append("删除文件:\n");
            Iterator<String> deletedDilesPath = deletedFilesSet.iterator();
            while (deletedDilesPath.hasNext()) {
                ps.append("  ").append(String.valueOf(deletedDilesPath.next())).append("\n");
            }
            ps.append("修改文件:\n");
            Iterator<String> changedFilesPath = changedFilesSet.iterator();
            while (changedFilesPath.hasNext()) {
                ps.append("  ").append(String.valueOf(changedFilesPath.next())).append("\n");
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        long endTime=System.currentTimeMillis(); //获取结束时间
        System.out.println("校核完成 耗时:" + (endTime - startTime) + "ms");

    }

3.多线程版本

分别写三个个线程,一个线程做增加文件的校验,一个做修改文件的校验,一个文件做删除文件的校验

新增文件校验线程
    public class newFilePathCheck implements Runnable{

        private String oldPath;
        private String newPath;

        private Set<String> newFilesSet;

        public newFilePathCheck(String oldPath, String newPath, Set<String> newFilesSet) {
            this.oldPath = oldPath;
            this.newPath = newPath;
            this.newFilesSet = newFilesSet;
        }

        @Override
        public void run() {
            long startTime = System.currentTimeMillis();  //获取开始时间

            // 遍历新文件目录
            Iterator iterator = FileUtils.iterateFiles(new File(newPath), null, true);

            try {
                // 遍历比较新旧目录
                // 1. 如果文件不存在,则说明是新增的文件,放到newFilesSet
                // 2. 如果MD5值不一样,则文件被修改,放到changedFilesSet
                while (iterator.hasNext()) {
                    // 新文件路径
                    String nPathStr = iterator.next().toString();
                    File newFile = new File(nPathStr);
                    // 旧文件路径
                    File oldFile = new File(nPathStr.replace(newPath, oldPath));
                    // 判断文件是否存在
                    if (!oldFile.exists()) {
                        newFilesSet.add(newFile.toString());
                    }
                }
            }catch (Exception e) {
                System.err.println("发生异常!");
            }

            long endTime=System.currentTimeMillis(); //获取结束时间
            System.out.println("新增文件分析完成 耗时:" + (endTime - startTime) + "ms");
        }
    }
修改文件校验线程
  public class changedFilePathCheck implements Runnable{

        private String oldPath;
        private String newPath;

        private Set<String> changedFilesSet;

        public changedFilePathCheck(String oldPath, String newPath, Set<String> changedFilesSet) {
            this.oldPath = oldPath;
            this.newPath = newPath;
            this.changedFilesSet = changedFilesSet;
        }

        @Override
        public void run() {
            long startTime = System.currentTimeMillis();  //获取开始时间

            // 遍历新文件目录
            Iterator iterator = FileUtils.iterateFiles(new File(newPath), null, true);

            try {
                // 遍历比较新旧目录
                // 1. 如果文件不存在,则说明是新增的文件,放到newFilesSet
                // 2. 如果MD5值不一样,则文件被修改,放到changedFilesSet
                while (iterator.hasNext()) {
                    // 新文件路径
                    String nPathStr = iterator.next().toString();
                    File newFile = new File(nPathStr);
                    // 旧文件路径
                    File oldFile = new File(nPathStr.replace(newPath, oldPath));
                    // 判断文件是否存在
                    if (oldFile.exists()) {
                        // MD5校验
                        // 如果不相同,则文件被修改,放到changedFilesSet
                        String newMD5 = getMD5(newFile);
                        String oldMD5 = getMD5(oldFile);
                        if (!StringUtils.equals( newMD5, oldMD5 )) {
                            changedFilesSet.add(newFile.toString());
                        }
                    }
                }
            }catch (Exception e) {
                System.err.println("发生异常!");
            }

            long endTime=System.currentTimeMillis(); //获取结束时间
            System.out.println("修改文件分析完成 耗时:" + (endTime - startTime) + "ms");
        }
    }
删除文件校验教程
  public class oldFilePathCheck implements Runnable{

        private String oldPath;
        private String newPath;
        private Set<String> deletedFilesSet;

        public oldFilePathCheck(String oldPath, String newPath, Set<String> deletedFilesSet) {
            this.oldPath = oldPath;
            this.newPath = newPath;
            this.deletedFilesSet = deletedFilesSet;
        }

        @Override
        public void run() {

            long startTime = System.currentTimeMillis();  //获取开始时间

            // 遍历旧文件目录
            Iterator iterator1 = FileUtils.iterateFiles(new File(oldPath), null, true);

            try {
                // 遍历旧的文件目录,用于查找被删除的文件
                while (iterator1.hasNext()) {
                    // 旧文件路径
                    String oPathStr = iterator1.next().toString();
                    // 新文件路径
                    File newFile = new File(oPathStr.replace(oldPath, newPath));
                    if (!newFile.exists()) {
                        deletedFilesSet.add(oPathStr);
                    }
                }
            } catch (Exception e) {
                System.err.println("发生异常!");
            }

            long endTime=System.currentTimeMillis(); //获取结束时间
            System.out.println("删除文件分析完成 耗时:" + (endTime - startTime) + "ms");
        }
    }
最后是主线程
   public void checkFile(String oldPath, String newPath,String outPath) throws InterruptedException, IOException {

        long startTime = System.currentTimeMillis();  //获取开始时间

        //三个set,分别放置更改文件、新增文件、删除文件
        Set<String> changedFilesSet = new HashSet<>();
        Set<String> newFilesSet = new HashSet<>();
        Set<String> deletedFilesSet = new HashSet<>();

        Thread newFilePathCheck = new Thread(new newFilePathCheck(oldPath, newPath, newFilesSet));
        Thread changedFilePathCheck = new Thread(new changedFilePathCheck(oldPath, newPath, changedFilesSet));
        Thread oldFilePathCheck = new Thread(new oldFilePathCheck(oldPath, newPath, deletedFilesSet));

        newFilePathCheck.start();
        changedFilePathCheck.start();
        oldFilePathCheck.start();

        //等三个线程都执行完毕再执行主线程
        newFilePathCheck.join();
        changedFilePathCheck.join();
        oldFilePathCheck.join();
        
        //创建文件
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
        String fileName = outPath + "\\" + df.format(new Date()) + ".txt";
        //写入txt文件
        File file = new File(fileName);
        boolean newFile = file.createNewFile();

        try {
            PrintStream ps = new PrintStream(new FileOutputStream(file));
            ps.append("旧文件目录:").append(oldPath).append("\n");
            ps.append("新文件目录:").append(newPath).append("\n");
            ps.append("新增文件:\n");
            Iterator<String> newFilesPath = newFilesSet.iterator();
            while (newFilesPath.hasNext()) {
                ps.append("  ").append(String.valueOf(newFilesPath.next())).append("\n");
            }
            ps.append("删除文件:\n");
            Iterator<String> deletedDilesPath = deletedFilesSet.iterator();
            while (deletedDilesPath.hasNext()) {
                ps.append("  ").append(String.valueOf(deletedDilesPath.next())).append("\n");
            }
            ps.append("修改文件:\n");
            Iterator<String> changedFilesPath = changedFilesSet.iterator();
            while (changedFilesPath.hasNext()) {
                ps.append("  ").append(String.valueOf(changedFilesPath.next())).append("\n");
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        long endTime=System.currentTimeMillis(); //获取结束时间
        System.out.println("多线程校核完成 耗时:" + (endTime - startTime) + "ms");
    }

4.对比一下两种方法的时间耗费

   public static void main( String[] args ) throws IOException, InterruptedException {

        String oldPath = "D:\\test\\oldPath";
        String newPath = "D:\\test\\newPath";
        String outPath = "D:\\test\\outPath";

        FileCheckSingleThread fileCheckSingleThread = new FileCheckSingleThread();
        fileCheckSingleThread.checkFile(oldPath, newPath, outPath);

        FileCheckMultiThread fileCheckMultiThread = new FileCheckMultiThread();
        fileCheckMultiThread.checkFile(oldPath, newPath, outPath);

    }
单线程校核完成 耗时:1569ms
新增文件分析完成 耗时:414ms
删除文件分析完成 耗时:434ms
修改文件分析完成 耗时:1031ms
多线程校核完成 耗时:1042ms

可以看到由于并行执行,虽然所有线程的执行时间加起来是比原来的要多,但是总体时间减少了,算是充分利用了cpu资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值