zoukankan      html  css  js  c++  java
  • 一个diff工具,用于判断两个目录下所有的改动(比较新旧版本文件夹)

    需求:

      编写一个diff工具,用于判断两个目录下所有的改动

    详细介绍:

    1. 有A和B两个目录,目录所在位置及层级均不确定
    2. 需要以B为基准找出两个目录中所有有改动的文件(文件或内容增加、修改、删除),将有改动的文件放入第三个目录中,层级结构与原目录相同
    3. 将所有新增与更新信息记录到更新日志文件中
    4. 将删除信息单独记录到删除日志文件中
    5. 每次执行diff工具需要生成一个新的以日期命名的目录存放文件

    使用场景:

      本工具用于软件版本升级时找出两个版本间所有修改过的文件,便于增量替换。

    提示:    使用CRC判断文件是否改动

    依赖的Jar包:

    代码如下:

    package test2;
    
    import java.io.File;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import org.apache.commons.io.FileUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class DiffUtil {
    
        private static Logger logger = LoggerFactory.getLogger(DiffUtil.class);// slf4j的日志记录器
    
        /**
         * 对比文件
         * @param oldDir    旧版本文件(需求中的A文件夹)
         * @param nowDir    新版本文件(需求中的B文件夹)
         * @param diffDir   生成对比结果的文件夹(需求中的change文件夹)
         */
        public static void compareFile(String oldDir, String nowDir, String diffDir) {
            long startTime = System.currentTimeMillis();// 开始时间
            // 1.在change文件夹下面生成一个当前日期格式的文件夹
            String currentTime = convertCurrentTime2String();
            String fileAndLogDir = diffDir + "\" + currentTime;// 存放日志和更新后的文件的目录
            File fileDiffDir = new File(fileAndLogDir);
            fileDiffDir.mkdirs();
            
            // 2.获取旧版本文件夹下和新版本文件夹下面的文件的CRC校验码
            Map<String, Long> oldFileCRCs = getAllFileNameAndCRC(oldDir, oldDir,
                    new HashMap<String, Long>());
            Map<String, Long> nowFileCRCs = getAllFileNameAndCRC(nowDir, nowDir,
                    new HashMap<String, Long>());
            
            // 3.遍历删除的文件且将日志信息输出到deleteFile.log
            String deleteLogName = "deleteFile.log";
            File deleteLogFile = new File(fileDiffDir, deleteLogName);
            // 3.1遍历旧文件夹下面的map的key,如果在新文件夹的map中找不到匹配的key值,证明是删除文件了
            logger.info("----开始记录删除日志:" + convertCurrentTime2String() + "----");
            try {
                FileUtils.write(deleteLogFile, "-----开始记录删除日志:"
                        + convertCurrentTime2String() + "----
    ", "UTF-8", true);
            } catch (IOException e) {
                logger.error("将删除日志写入文件deteFile.log出错", e);
            }
            
            List<String> deleteFileNames = new ArrayList<String>();
            for (String oldKey : oldFileCRCs.keySet()) {
                if (!nowFileCRCs.containsKey(oldKey)) {
                    logger.info("删除文件	" + oldKey);
                    try {
                        FileUtils.write(deleteLogFile, "删除文件	" + oldKey + "
    ",
                                "UTF-8", true);
                    } catch (IOException e) {
                        logger.error("将删除日志写入文件deteFile.log出错", e);
                    }
                    deleteFileNames.add(oldKey);
                }
            }
            
            try {
                FileUtils.write(deleteLogFile, "
    ", "UTF-8", true);
                FileUtils.write(deleteLogFile, "---------删除文件日志结束:共删除"
                        + deleteFileNames.size() + "个文件----" + "
    ", "UTF-8",
                        true);
            } catch (IOException e) {
                logger.error("将删除日志的统计信息写入文件deteFile.log出错", e);
            }
            logger.info("-----删除文件日志结束:共删除" + deleteFileNames.size() + "个文件----");
            
            // 4.遍历增加和更新的文件
            String addAndUpdateLogName = "addAndUpdate.log";
            File addUpdateLogFile = new File(fileDiffDir, addAndUpdateLogName);
            logger.info("-----开始记录增加、更新日志------");
            List<String> addFileNames = new ArrayList<String>();// 增加文件名字集合
            List<String> updateFileNames = new ArrayList<String>();// 更新文件名字集合
            for (String nowKey : nowFileCRCs.keySet()) {
                if (!oldFileCRCs.containsKey(nowKey)) {
                    addFileNames.add(nowKey);
                } else {
                    if (oldFileCRCs.get(nowKey).equals(nowFileCRCs.get(nowKey))) {
                        continue;
                    }
                    updateFileNames.add(nowKey);
                }
            }
            
            // 4.1新增文件写入日志
            try {
                FileUtils.write(addUpdateLogFile, "-----Diff时间:"
                        + convertCurrentTime2String() + "----" + "
    ", "UTF-8",
                        true);
                FileUtils.write(addUpdateLogFile, "
    ", "UTF-8", true);
                FileUtils.write(addUpdateLogFile, "----共新增文件" + addFileNames.size()
                        + "个----
    ", "UTF-8", true);
                logger.info("----共新增文件" + addFileNames.size() + "个----");
            } catch (IOException e1) {
                logger.error("将新增信息写入文件addAndUpdate.log出错", e1);
            }
            
            for (String addFileName : addFileNames) {
                try {
                    logger.info("增加了文件" + addFileName);
                    FileUtils.write(addUpdateLogFile, "增加了文件" + addFileName
                            + "
    ", "UTF-8", true);
                } catch (IOException e) {
                    logger.error("将新增信息写入文件addAndUpdate.log出错", e);
                }
            }
    
            // 4.2更新信息写入日志
            try {
                FileUtils.write(addUpdateLogFile, "
    ", "UTF-8", true);
                FileUtils.write(addUpdateLogFile,
                        "----共更新文件" + updateFileNames.size() + "个----
    ",
                        "UTF-8", true);
                logger.info("----共更新文件" + updateFileNames.size() + "个----");
            } catch (IOException e) {
                logger.error("将更新信息写入文件addAndUpdate.log出错", e);
            }
            for (String updateFileName : updateFileNames) {
                try {
                    FileUtils.write(addUpdateLogFile, "更新了文件" + updateFileName
                            + "
    ", "UTF-8", true);
                    logger.info("更新了文件" + updateFileName);
                } catch (IOException e) {
                    logger.error("将更新信息写入文件addAndUpdate.log出错", e);
                }
            }
            // 5.将有新增/更新的文件放入第三个目录中(文件拷贝)
            filesCopy(addFileNames, nowDir, diffDir + "\"+ currentTime);
            filesCopy(updateFileNames, nowDir, diffDir + "\"+ currentTime);
            long endTime = System.currentTimeMillis();// 结束时间
            logger.info("----运行结束,耗时" + (endTime - startTime) + "ms----");
            // 6.写入程序运行时间到日志文件
            try {
                FileUtils.write(addUpdateLogFile, "----运行结束,耗时"
                        + (endTime - startTime) + "ms----" + "
    ", "UTF-8", true);
                FileUtils.write(deleteLogFile, "----运行结束,耗时"
                        + (endTime - startTime) + "ms----" + "
    ", "UTF-8", true);
            } catch (IOException e) {
                logger.error("将运行耗时写入日志文件出错", e);
            }
        }
    
        /**
         * 将新增的文件和更新的文件复制到第三个文件夹(开源jar包实现文件拷贝)
         * @param fileNames 文件名字集合
         * @param nowDir    当前所在的目录
         * @param diffDir    目的目录
         */
        private static void filesCopy(List<String> fileNames,
                String nowDir, String diffDir) {
            File srcFile = null,destFile = null , destFileDir = null;
            for (String sourceFileName : fileNames) {
                srcFile = new File(nowDir+"\"+sourceFileName);
                destFile = new File(diffDir, sourceFileName);
                String fileName = srcFile.getName();
                destFileDir = new File((diffDir + "\" + sourceFileName).replace(
                        fileName, ""));
                destFileDir.mkdirs();
                try {
                    FileUtils.copyFile(srcFile, destFile);
                } catch (IOException e) {
                    logger.error("复制文件出错",e);
                }
            }
        }
    
        /**
         * 获取指定文件夹下面的所有文件,key是文件的名字(去掉基层路径),value是CRC冗余检验码(递归遍历)
         * @param baseDir      基层路径
         * @param fileDir      真实文件名字(去掉基层路径形成key)
         * @param resultMap    结果(所有文件的CRC32码,key是真实文件名去掉基层路径,Value是CRC32码)
         * @return    所有文件的CRC32码,key是真实文件名去掉基层路径,Value是CRC32码
         */
        private static Map<String, Long> getAllFileNameAndCRC(String baseDir,
                String fileDir, Map<String, Long> resultMap) {
            File file = new File(fileDir);
            if (!file.exists()) {// 文件不存在直接返回
                return null;
            }
            
            if (file.isDirectory()) {// 如果是目录,继续递归遍历获取其下面的所有文件的CRC32码
                for (File f : file.listFiles()) {
                    getAllFileNameAndCRC(baseDir, f.getAbsolutePath(), resultMap);
                }
            } else {// 如果是文件,获取文件的CRC32码并添加到map中
                long fileCRC = 0l;
                try {
                    fileCRC = FileUtils.checksumCRC32(file);
                } catch (IOException e) {
                    logger.error("获取文件的CRC32出错",e);
                }
                resultMap.put(file.getAbsolutePath().replace(baseDir, ""), fileCRC);
            }
            
            return resultMap;
        }
    
        /**
         * 将当前日期转换为指定格式的字符串
         * @return yyyy年MM月dd日HH时mm分ss秒 格式的日期串
         */
        private static String convertCurrentTime2String() {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
            return sdf.format(new Date());
        }
    
    }

    测试:

    package test2;
    
    public class MyTest {
    
        public static void main(String[] args) {
            // 1.第一种测试方式,直接将需要对比的文件夹写死在程序中运行
            String oldDir = "C:\Users\Administrator\Desktop\mytest\A";
            String nowDir = "C:\Users\Administrator\Desktop\mytest\B";
            String diffDir = "C:\Users\Administrator\Desktop\mytest\change";
    
            //第二种方式,cmd窗口传参数进行运行
            /*if (args == null || args.length != 3) {
                System.out
                        .println("参数不全,使用方式java -jar DiffUtils.jar 原路径名 新路径名 diff目录路径");
                return;
            }
            String oldDir = args[0];
            String nowDir = args[1];
            String diffDir = args[2];*/
            DiffUtil.compareFile(oldDir, nowDir, diffDir);
        }
    
    }

     我已经将此工具作为一个jar包打包起来,下载地址: http://qiaoliqiang.cn/fileDown/DiffUtil.jar

    运行方式:

    java -jar DiffUtil.jar C:UsersliqiangDesktop新建文件件夹考核1 C:UsersliqiangDesktop新建文件夹change

     总结:

      1. 文件复制有多种方式,可以用    FileUtils.copyFile(srcFile, destFile);  只需要传递两个File参数,第一个是源文件,第二个是目的文件。

                  也可以用   IOUtils.copy(inputStream, outputStream);  传递两个参数,第一个输入流,第二个是输出流。

      2.  获取文件的CRC32循环冗余检验码也有多种方式,可以直接用   FileUtils.checksumCRC32(file);   直接获取

                              也可以用下面的工具方法获取:

    /**
      * 获取文件的CRC
      * 
      * @param file
      *            需要获取CRC32码的文件
      * @return 文件的CRC32循环冗余码
      */
     private static long getFileCRC(File file) {
      BufferedInputStream bsrc = null;
      CRC32 crc = new CRC32();
      try {
       bsrc = new BufferedInputStream(new FileInputStream(file));
       byte[] bytes = new byte[1024];
       int i;
       while ((i = bsrc.read(bytes)) != -1) {
        crc.update(bytes, 0, i);
       }
      } catch (Exception e) {
       logger.error("计算文件的CRC32循环冗余检验出错", e);
      } finally {
       if (bsrc != null) {
        try {
         bsrc.close();
        } catch (IOException e) {
         logger.error("计算文件的CRC32循环冗余检验出错", e);
        }
       }
      }
      return crc.getValue();
     }

       3.日志记录也有多种方法,第一种使用log4j,获取logger的方法如下:   Logger logger = Logger.getLogger(ApArrangeCourseAuditController.class);

                  第二种使用slf4j,获取logger的方法如下:    private Logger logger = LoggerFactory.getLogger(ExtUserController.class);

  • 相关阅读:
    Mini2440裸机开发之MMU
    Mini2440裸机开发之IIC
    Mini2440裸机开发之SPI
    Mini2440裸机开发之存储器控制器
    Mini2440裸机开发之模数转换开发
    Mini2440裸机开发之LCD编程(GB2312、ASCII字库制作)
    Python pandas df.iloc[:, 0] 取确定列值作双轴图
    python plot 画双坐标轴 设置百分比展示 和字体等细节
    python Sqlserver数据库画双轴图
    windows下Idea中用Python访问oracle数据库的方法
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/9262637.html
Copyright © 2011-2022 走看看