需求:

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

详细介绍:

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

使用场景:

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

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

依赖的Jar包:

代码如下:

  1. package test2;
  2.  
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.text.SimpleDateFormat;
  6. import java.util.ArrayList;
  7. import java.util.Date;
  8. import java.util.HashMap;
  9. import java.util.List;
  10. import java.util.Map;
  11. import org.apache.commons.io.FileUtils;
  12. import org.slf4j.Logger;
  13. import org.slf4j.LoggerFactory;
  14.  
  15. public class DiffUtil {
  16.  
  17. private static Logger logger = LoggerFactory.getLogger(DiffUtil.class);// slf4j的日志记录器
  18.  
  19. /**
  20. * 对比文件
  21. * @param oldDir 旧版本文件(需求中的A文件夹)
  22. * @param nowDir 新版本文件(需求中的B文件夹)
  23. * @param diffDir 生成对比结果的文件夹(需求中的change文件夹)
  24. */
  25. public static void compareFile(String oldDir, String nowDir, String diffDir) {
  26. long startTime = System.currentTimeMillis();// 开始时间
  27. // 1.在change文件夹下面生成一个当前日期格式的文件夹
  28. String currentTime = convertCurrentTime2String();
  29. String fileAndLogDir = diffDir + "\\" + currentTime;// 存放日志和更新后的文件的目录
  30. File fileDiffDir = new File(fileAndLogDir);
  31. fileDiffDir.mkdirs();
  32.  
  33. // 2.获取旧版本文件夹下和新版本文件夹下面的文件的CRC校验码
  34. Map<String, Long> oldFileCRCs = getAllFileNameAndCRC(oldDir, oldDir,
  35. new HashMap<String, Long>());
  36. Map<String, Long> nowFileCRCs = getAllFileNameAndCRC(nowDir, nowDir,
  37. new HashMap<String, Long>());
  38.  
  39. // 3.遍历删除的文件且将日志信息输出到deleteFile.log
  40. String deleteLogName = "deleteFile.log";
  41. File deleteLogFile = new File(fileDiffDir, deleteLogName);
  42. // 3.1遍历旧文件夹下面的map的key,如果在新文件夹的map中找不到匹配的key值,证明是删除文件了
  43. logger.info("----开始记录删除日志:" + convertCurrentTime2String() + "----");
  44. try {
  45. FileUtils.write(deleteLogFile, "-----开始记录删除日志:"
  46. + convertCurrentTime2String() + "----\r\n", "UTF-8", true);
  47. } catch (IOException e) {
  48. logger.error("将删除日志写入文件deteFile.log出错", e);
  49. }
  50.  
  51. List<String> deleteFileNames = new ArrayList<String>();
  52. for (String oldKey : oldFileCRCs.keySet()) {
  53. if (!nowFileCRCs.containsKey(oldKey)) {
  54. logger.info("删除文件\t" + oldKey);
  55. try {
  56. FileUtils.write(deleteLogFile, "删除文件\t" + oldKey + "\r\n",
  57. "UTF-8", true);
  58. } catch (IOException e) {
  59. logger.error("将删除日志写入文件deteFile.log出错", e);
  60. }
  61. deleteFileNames.add(oldKey);
  62. }
  63. }
  64.  
  65. try {
  66. FileUtils.write(deleteLogFile, "\r\n", "UTF-8", true);
  67. FileUtils.write(deleteLogFile, "---------删除文件日志结束:共删除"
  68. + deleteFileNames.size() + "个文件----" + "\r\n", "UTF-8",
  69. true);
  70. } catch (IOException e) {
  71. logger.error("将删除日志的统计信息写入文件deteFile.log出错", e);
  72. }
  73. logger.info("-----删除文件日志结束:共删除" + deleteFileNames.size() + "个文件----");
  74.  
  75. // 4.遍历增加和更新的文件
  76. String addAndUpdateLogName = "addAndUpdate.log";
  77. File addUpdateLogFile = new File(fileDiffDir, addAndUpdateLogName);
  78. logger.info("-----开始记录增加、更新日志------");
  79. List<String> addFileNames = new ArrayList<String>();// 增加文件名字集合
  80. List<String> updateFileNames = new ArrayList<String>();// 更新文件名字集合
  81. for (String nowKey : nowFileCRCs.keySet()) {
  82. if (!oldFileCRCs.containsKey(nowKey)) {
  83. addFileNames.add(nowKey);
  84. } else {
  85. if (oldFileCRCs.get(nowKey).equals(nowFileCRCs.get(nowKey))) {
  86. continue;
  87. }
  88. updateFileNames.add(nowKey);
  89. }
  90. }
  91.  
  92. // 4.1新增文件写入日志
  93. try {
  94. FileUtils.write(addUpdateLogFile, "-----Diff时间:"
  95. + convertCurrentTime2String() + "----" + "\r\n", "UTF-8",
  96. true);
  97. FileUtils.write(addUpdateLogFile, "\r\n", "UTF-8", true);
  98. FileUtils.write(addUpdateLogFile, "----共新增文件" + addFileNames.size()
  99. + "个----\r\n", "UTF-8", true);
  100. logger.info("----共新增文件" + addFileNames.size() + "个----");
  101. } catch (IOException e1) {
  102. logger.error("将新增信息写入文件addAndUpdate.log出错", e1);
  103. }
  104.  
  105. for (String addFileName : addFileNames) {
  106. try {
  107. logger.info("增加了文件" + addFileName);
  108. FileUtils.write(addUpdateLogFile, "增加了文件" + addFileName
  109. + "\r\n", "UTF-8", true);
  110. } catch (IOException e) {
  111. logger.error("将新增信息写入文件addAndUpdate.log出错", e);
  112. }
  113. }
  114.  
  115. // 4.2更新信息写入日志
  116. try {
  117. FileUtils.write(addUpdateLogFile, "\r\n", "UTF-8", true);
  118. FileUtils.write(addUpdateLogFile,
  119. "----共更新文件" + updateFileNames.size() + "个----\r\n",
  120. "UTF-8", true);
  121. logger.info("----共更新文件" + updateFileNames.size() + "个----");
  122. } catch (IOException e) {
  123. logger.error("将更新信息写入文件addAndUpdate.log出错", e);
  124. }
  125. for (String updateFileName : updateFileNames) {
  126. try {
  127. FileUtils.write(addUpdateLogFile, "更新了文件" + updateFileName
  128. + "\r\n", "UTF-8", true);
  129. logger.info("更新了文件" + updateFileName);
  130. } catch (IOException e) {
  131. logger.error("将更新信息写入文件addAndUpdate.log出错", e);
  132. }
  133. }
  134. // 5.将有新增/更新的文件放入第三个目录中(文件拷贝)
  135. filesCopy(addFileNames, nowDir, diffDir + "\\"+ currentTime);
  136. filesCopy(updateFileNames, nowDir, diffDir + "\\"+ currentTime);
  137. long endTime = System.currentTimeMillis();// 结束时间
  138. logger.info("----运行结束,耗时" + (endTime - startTime) + "ms----");
  139. // 6.写入程序运行时间到日志文件
  140. try {
  141. FileUtils.write(addUpdateLogFile, "----运行结束,耗时"
  142. + (endTime - startTime) + "ms----" + "\r\n", "UTF-8", true);
  143. FileUtils.write(deleteLogFile, "----运行结束,耗时"
  144. + (endTime - startTime) + "ms----" + "\r\n", "UTF-8", true);
  145. } catch (IOException e) {
  146. logger.error("将运行耗时写入日志文件出错", e);
  147. }
  148. }
  149.  
  150. /**
  151. * 将新增的文件和更新的文件复制到第三个文件夹(开源jar包实现文件拷贝)
  152. * @param fileNames 文件名字集合
  153. * @param nowDir 当前所在的目录
  154. * @param diffDir 目的目录
  155. */
  156. private static void filesCopy(List<String> fileNames,
  157. String nowDir, String diffDir) {
  158. File srcFile = null,destFile = null , destFileDir = null;
  159. for (String sourceFileName : fileNames) {
  160. srcFile = new File(nowDir+"\\"+sourceFileName);
  161. destFile = new File(diffDir, sourceFileName);
  162. String fileName = srcFile.getName();
  163. destFileDir = new File((diffDir + "\\" + sourceFileName).replace(
  164. fileName, ""));
  165. destFileDir.mkdirs();
  166. try {
  167. FileUtils.copyFile(srcFile, destFile);
  168. } catch (IOException e) {
  169. logger.error("复制文件出错",e);
  170. }
  171. }
  172. }
  173.  
  174. /**
  175. * 获取指定文件夹下面的所有文件,key是文件的名字(去掉基层路径),value是CRC冗余检验码(递归遍历)
  176. * @param baseDir 基层路径
  177. * @param fileDir 真实文件名字(去掉基层路径形成key)
  178. * @param resultMap 结果(所有文件的CRC32码,key是真实文件名去掉基层路径,Value是CRC32码)
  179. * @return 所有文件的CRC32码,key是真实文件名去掉基层路径,Value是CRC32码
  180. */
  181. private static Map<String, Long> getAllFileNameAndCRC(String baseDir,
  182. String fileDir, Map<String, Long> resultMap) {
  183. File file = new File(fileDir);
  184. if (!file.exists()) {// 文件不存在直接返回
  185. return null;
  186. }
  187.  
  188. if (file.isDirectory()) {// 如果是目录,继续递归遍历获取其下面的所有文件的CRC32码
  189. for (File f : file.listFiles()) {
  190. getAllFileNameAndCRC(baseDir, f.getAbsolutePath(), resultMap);
  191. }
  192. } else {// 如果是文件,获取文件的CRC32码并添加到map中
  193. long fileCRC = 0l;
  194. try {
  195. fileCRC = FileUtils.checksumCRC32(file);
  196. } catch (IOException e) {
  197. logger.error("获取文件的CRC32出错",e);
  198. }
  199. resultMap.put(file.getAbsolutePath().replace(baseDir, ""), fileCRC);
  200. }
  201.  
  202. return resultMap;
  203. }
  204.  
  205. /**
  206. * 将当前日期转换为指定格式的字符串
  207. * @return yyyy年MM月dd日HH时mm分ss秒 格式的日期串
  208. */
  209. private static String convertCurrentTime2String() {
  210. SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
  211. return sdf.format(new Date());
  212. }
  213.  
  214. }

测试:

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

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

运行方式:

  1. 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);   直接获取

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

  1. /**
  2. * 获取文件的CRC
  3. *
  4. * @param file
  5. * 需要获取CRC32码的文件
  6. * @return 文件的CRC32循环冗余码
  7. */
  8. private static long getFileCRC(File file) {
  9. BufferedInputStream bsrc = null;
  10. CRC32 crc = new CRC32();
  11. try {
  12. bsrc = new BufferedInputStream(new FileInputStream(file));
  13. byte[] bytes = new byte[1024];
  14. int i;
  15. while ((i = bsrc.read(bytes)) != -1) {
  16. crc.update(bytes, 0, i);
  17. }
  18. } catch (Exception e) {
  19. logger.error("计算文件的CRC32循环冗余检验出错", e);
  20. } finally {
  21. if (bsrc != null) {
  22. try {
  23. bsrc.close();
  24. } catch (IOException e) {
  25. logger.error("计算文件的CRC32循环冗余检验出错", e);
  26. }
  27. }
  28. }
  29. return crc.getValue();
  30. }

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

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

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

  1. 代码实现:判断E盘目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称

    package com.loaderman.test; import java.io.File; import java.io.FilenameFilter; public class Test { ...

  2. Shell 实现找出两个目录下的同名文件方法

    # 首先我们来创建一些 2 个目录,里面的目录结构及相关文件如下所示: # 从上面的测试目录可以看到, lol.txt lol2.txt 两个文件是两个目录下的同名文件 # 有实际例子,思路就容易出来 ...

  3. [No00006B]方便的网络下载工具wget 可下载网站目录下的所有文件(可下载整个网站)

    wget是linux下命令行的下载工具,功能很强大,它能完成某些下载软件所不能做的,比如如果你想下载一个网页目录下的所有文件,如何做呢?网络用户有时候会遇到需要下载一批文件的情况,有时甚至需要把整个网 ...

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

    http://zhidao.baidu.com/link?url=DkxU9CyhJb_dIUAPCmPmxRtQsENgCzqy5qnLPEj_V9DqNzdt6Qya0U5iCVRCYFkgoRo ...

  5. 文件名命工具类(将指定目录下的文件的type类型的文件,进行重命名,命名后的文件将去掉type)

    import java.io.File; /** * <b>function:</b> 文件命名工具类 * @author hoojo * @createDate 2012-5 ...

  6. 用Python删除本地目录下某一时间点之前创建的所有文件

    因为工作原因,需要定期清理某个文件夹下面创建时间超过1年的所有文件,所以今天集中学习了一下Python对于本地文件及文件夹的操作.网上 这篇文章 简明扼要地整理出最常见的os方法,抄袭如下: os.l ...

  7. 2.每人自己建立一个HelloWorld项目,练习使用git的add/commit/push/pull/fetch/clone等基本命令。比较项目的新旧版本的差别。答题人:张立鹏

    第1步:创建SSH Key.在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步.如果没有,打开Shell ...

  8. 第二章——建立一个HelloWorld项目,练习使用git的add/commit/push/pull/fetch/clone等基本命令。比较项目的新旧版本的差别-----答题者:徐潇瑞

    1.首先下载安装git,很简单所以就不详细说了,当弹出一个类似的命令窗口的东西,就说明Git安装成功 2.因为Git是分布式版本控制系统,所以需要填写用户名和邮箱作为一个标识 3.接着,注册githu ...

  9. Pycharm学习记录---同一目录下无法import明明已经存在的.py文件

    转自:https://blog.csdn.net/l8947943/article/details/79874180 问题描述: 如图:同目录下明明存在相应文件,在导入时却出现带有红色波浪线,说没有相 ...

随机推荐

  1. delphi ERP框架

    之前做c/s架构,接了有家装饰的一个ERP项目,做了一个ERP框架,现在转后端开发了,这些东西还是蛮怀念的,就开源出来吧,有需要的同学可以参考. https://github.com/qianlnk/ ...

  2. 重新认识javascript的settimeout和异步

    1.简单的settimeout setTimeout(function () { while (true) { } }, 1000); setTimeout(function () { alert(' ...

  3. python自动化之连接数据库

    # -*- coding: utf-8 -*- """ Created on Fri Mar 20 10:50:56 2015 @author: sl "&qu ...

  4. BZOJ5288 HNOI/AHOI2018游戏

    首先将之间没有锁的房间合并.显然可达性具有传递性和反交换律(即若a能到达b,则b不能到达a). 考虑对每个房间找到其左右第一个(即与其最接近的)能作为起点到达它的房间.如果能求出这个,对此建两棵树,问 ...

  5. python使用selenium、PhantomJS获得网站cookie信息#windows

    首先python安装selenium,命令行中输入 pip install selenium 在执行代码如下代码时出现错误 driver=webdriver.PhantomJS() 错误如下 sele ...

  6. java10 新特性 详解

    引言: 点击-->java9 新特性 详解 点击-->java8 新特性 详解 正题: 1.局部变量var 将前端思想var关键字引入java后段,自动检测所属于类型,一种情况除外,不能为 ...

  7. PHP 压缩图片质量

    $imageFileName = './test2.jpg';$uploadfile_resize = $imageFileName;$pic_width_max = 1000;$pic_height ...

  8. CentOS 6.6搭建LNMP环境

    一.安装前 1.关闭linux的安全机制 vim /etc/selinux/config SELINUX=enforcing  改为  SELINUX=disabled 2.关闭iptables防火墙 ...

  9. getopt_long

    http://blog.csdn.net/lanyan822/article/details/7692013 在程序中难免需要使用命令行选项,可以选择自己解析命令行选项,但是有现成的,何必再造轮子.下 ...

  10. 解决ImportError: cannot import name HTTPSHandler

    /usr/local/python3.5/bin/pip3.5 install flask 的时候遇到了cannot import name HTTPSHandler 1. 原因在于openssl,o ...