一个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() + "----\r\n", "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("删除文件\t" + oldKey);
                try {
                    FileUtils.write(deleteLogFile, "删除文件\t" + oldKey + "\r\n",
                            "UTF-8", true);
                } catch (IOException e) {
                    logger.error("将删除日志写入文件deteFile.log出错", e);
                }
                deleteFileNames.add(oldKey);
            }
        }

        try {
            FileUtils.write(deleteLogFile, "\r\n", "UTF-8", true);
            FileUtils.write(deleteLogFile, "---------删除文件日志结束:共删除"
                    + deleteFileNames.size() + "个文件----" + "\r\n", "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() + "----" + "\r\n", "UTF-8",
                    true);
            FileUtils.write(addUpdateLogFile, "\r\n", "UTF-8", true);
            FileUtils.write(addUpdateLogFile, "----共新增文件" + addFileNames.size()
                    + "个----\r\n", "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
                        + "\r\n", "UTF-8", true);
            } catch (IOException e) {
                logger.error("将新增信息写入文件addAndUpdate.log出错", e);
            }
        }

        // 4.2更新信息写入日志
        try {
            FileUtils.write(addUpdateLogFile, "\r\n", "UTF-8", true);
            FileUtils.write(addUpdateLogFile,
                    "----共更新文件" + updateFileNames.size() + "个----\r\n",
                    "UTF-8", true);
            logger.info("----共更新文件" + updateFileNames.size() + "个----");
        } catch (IOException e) {
            logger.error("将更新信息写入文件addAndUpdate.log出错", e);
        }
        for (String updateFileName : updateFileNames) {
            try {
                FileUtils.write(addUpdateLogFile, "更新了文件" + updateFileName
                        + "\r\n", "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----" + "\r\n", "UTF-8", true);
            FileUtils.write(deleteLogFile, "----运行结束,耗时"
                    + (endTime - startTime) + "ms----" + "\r\n", "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:\Users\liqiang\Desktop\新建文件件夹\考核1 C:\Users\liqiang\Desktop\新建文件夹\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);

原文地址:https://www.cnblogs.com/qlqwjy/p/9262637.html

时间: 2024-10-13 20:24:54

一个diff工具,用于判断两个目录下所有的改动(比较新旧版本文件夹)的相关文章

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

没心情优化代码了,就先这样吧 1 package comcollection.test; 2 3 import java.io.BufferedInputStream; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java

统计两组数据的交集和补集(新旧数据的差异比较算法)遍历一次

旧数据A = {}新数据B = {} 新增项:B - A = { x | x∈B且x∉A}删除项:A - B = { x | x∈A且x∉B}共有项:B ∩ A = { x | x∈B且x∈A} import java.io.BufferedReader; import java.io.Closeable; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; im

第二章-第二题(每人自己建立一个HelloWorld项目,练习使用git的add/commit/push/pull/fetch/clone等基本命令。比较项目的新旧版本的差别。)--by侯伟婷

第二题:每人自己建立一个HelloWorld项目,练习使用git的add/commit/push/pull/fetch/clone等基本命令.比较项目的新旧版本的差别. 下面我将自己的练习结果和个人感受记录如下: 第一步:安装Git,设置自己的账号和邮箱,参见Git教程-廖雪峰的官方网站,网址如下参考资料1所示. 第二步:在Git中新建repository,名叫HelloWorld,并进行初始化,如图所示. 第三步:在HelloWorld版本库中新建了helloWorld.txt文件,用以练习G

linux中/etc与/var目录,各是什么意思?这两个目录下的文件有什么特点?

http://zhidao.baidu.com/link?url=DkxU9CyhJb_dIUAPCmPmxRtQsENgCzqy5qnLPEj_V9DqNzdt6Qya0U5iCVRCYFkgoRomoqUS_ZO7aIikccOTta /var 文件系统 /var 包含系统一般运行时要改变的数据.通常这些数据所在的目录的大小是要经常变化或扩充的.原来 /var 目录中有些内容是在 /usr 中的,但为了保持 /usr 目录的相对稳定,就把那些需要经常改变的目录放到 /var 中了.每个系统是

机器学习快速截图工具matlab版本——文件夹批量处理(原创)

简要说明: 1.打开文件夹后,遍历所有JPG格式图片,在同目录下新建一个CROP的文件夹存放裁剪的图片. 2.对每张图片, (1)初步框选你要裁剪的矩形框,会自动以你框选的左上点为起点,裁剪大小为长宽自动扩展,结果会自动保存值..\Crop文件夹 (2)图片show出来后,也可以不用框选,只需要心中想好你要裁剪图片的左上起点就好,然后点一下该点,就会自动save. %% use mouse to rect picture,and auto change to next picture % <机器

用nodejs把目录下所有用px做单位的css文件转化为用rem做单位的css文件

20171105 1211/星期日 公司为了更好适配手机端,以前用px做单位的css文件,全部需要转化为用rem做单位,目前是1rem=37.5px;开发新项目时,还是用习惯的px写样式代码,完成UI稿的还原后,再统一转化为用rem做单位,贴上我写的nodejs 代码: var fs = require('fs');var path=require('path'); console.log((__dirname))var oldContent='./px/';var newContent='./

UIColor延伸:判断两个颜色是否相等

不管UIColor使用CIColor,CGColor还是其他方式初始化的,其CGColor属性都是可用的.CoreGraphics中提供一个函数,用于判断两个CGColor是否相等,因此我们可以通过这个函数判断两个UIColor是否相等,下面是看一个简单的例子: // 判断两个颜色是否是同一种颜色if (CGColorEqualToColor([UIColor whiteColor].CGColor, [UIColor colorWithRed:1 green:1 blue:1 alpha:1]

每天一个Linux命令(23)--linux 目录结构(二)

二./usr 的意义与内容 依据FHS的基本定义, /usr 里面放置的数据属于可分享的与不可变动的(shareable,static), 如果你知道如何透过网络进行分区的挂载(例如在服务器篇会谈到的NFS服务器),那么/usr 确实可以分享给局域网络内的其它主机来使用. /usr 不是user的缩写,其实 usr 是Unix software resource 的缩写,也就是Unix 操作系统软件资源所放置的目录,而不是用户的数据啦.这点要注意,FHS建议所有软件开发者,应该讲他们的数据合理的

【CITE】C#目录、文件、文件夹操作

1.   在一个目录下创建一个文件夹 if (!System.IO.Directory.Exists(path)) System.IO.Directory.CreateDirectory(path); String path[email protected]”C:/新文件夹的名字” 2.   时间的字符串表示法   20080616091219 System.DateTime.Now.ToString("yyyyMMdd"), System.DateTime.Now.ToString(