编写一个diff工具,用于判断两个目录下所有的改动--2.0版本
package comcollection.test; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.channels.FileChannel; 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 java.util.zip.CRC32; import org.apache.commons.io.FileUtils; /** * * 1.有A和B两个目录,目录所在位置及层级均不确定 * 2.需要以B为基准找出两个目录中所有有改动的文件(文件或内容增加、修改、删除),将有改动的文件放入第三个目录中,层级结构与原目录相同 * 3.将所有新增与更新信息记录到更新日志文件中 * 4.将删除信息单独记录到删除日志文件中 * 5.每次执行diff工具需要生成一个新的以日期命名的目录存放文件 */ public class DiffUtils { public static void scanFileDirectory(String sourceFileUrl, String currentFileUrl, String diffFileUrl) { // 1. 在diffUrl目录下,创建日期格式的文件夹,用于存放改动的文件 // 1.1 获取当前日期 String nowTime = createCurrentTime(); // 1.2创建日期形式的文件目录 File fileDire = new File(diffFileUrl, nowTime); fileDire.mkdirs(); String diffFilePath = fileDire.getAbsolutePath(); // 2,遍历出删除的信息,并记录到删除日志文件中 // 2.1创建删除日志文件 String rootPath = fileDire.getAbsolutePath(); File deleteLogFile = new File(rootPath, "delete.log"); try { deleteLogFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); } // 2.2遍历出删除的信息 HashMap<String, Long> sourceUrlMap = getAllFileMap(sourceFileUrl); HashMap<String, Long> currentUrlMap = getAllFileMap(currentFileUrl); List<String> deleteFiles = new ArrayList<String>(); String deleteCurrentTime = createCurrentTime(); String diffMessage = "-------- Diff时间:" + deleteCurrentTime + " --------"; try { FileUtils.writeStringToFile(deleteLogFile, diffMessage + "\n", "UTF-8", true); } catch (IOException e1) { e1.printStackTrace(); } long start = System.currentTimeMillis(); int delFilecount = 0; for (Map.Entry<String, Long> sourceEntry : sourceUrlMap.entrySet()) { // 遍历原版本目录下的文件,如果该文件不在当前版本目录下面,即该文件被删除了 if (!currentUrlMap.containsKey(sourceEntry.getKey())) { deleteFiles.add(sourceEntry.getKey()); delFilecount++; } } long end = System.currentTimeMillis(); // 2.3 删除的信息记录到删除日志文件中 String fileCount = "-------- 共删除文件" + delFilecount + "个--------"; String endContent = "-------- 运行完毕,耗时:" + (end - start) + "ms"; try { FileUtils.writeLines(deleteLogFile, "UTF-8", deleteFiles, true); FileUtils.write(deleteLogFile, fileCount + "\n", "UTF-8", true); FileUtils.write(deleteLogFile, endContent + "\n", "UTF-8", true); } catch (IOException e) { e.printStackTrace(); } // 3.将所有新增与更新信息记录到更新日志文件中 // 3.1 创建新增/更新日志文件 File updateAndAddLogFile = new File(rootPath, "updateAndAdd.log"); try { updateAndAddLogFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); } // 3.2遍历新增与更新记录,并存入到集合中 List<String> addFiles = new ArrayList<String>(); List<String> updateFiles = new ArrayList<String>(); /** * addOrUpdateFileUrls的key-value分别为currentFileUrl文件的全路径名、 * diffFileUrl文件的全路径名 通过文件的完全路径可以获得文件对象,用于后面的文件复制操作 */ Map<String, String> addOrUpdateFileUrls = new HashMap<String, String>(); int addCount = 0; int updateCount = 0; long fileAddOrUpdatestart = System.currentTimeMillis(); for (Map.Entry<String, Long> currentUrlMapEntry : currentUrlMap.entrySet()) { String currentUrlSuffix = currentUrlMapEntry.getKey(); // 如果元素存在原来的目录下,并且crc相同,就是更新操作 if (sourceUrlMap.containsKey(currentUrlMapEntry.getKey())) { try { long sourceUrlCrc = sourceUrlMap.get(currentUrlMapEntry.getKey()); long currentUrlCrc = currentUrlMapEntry.getValue(); if (currentUrlCrc != sourceUrlCrc) { updateCount++; updateFiles.add(currentUrlSuffix); addOrUpdateFileUrls.put(currentFileUrl + currentUrlSuffix, diffFilePath + currentUrlSuffix); } } catch (Exception e) { e.printStackTrace(); } } else {// 如果元素不存在A目录下,就是新增的元素 addCount++; addFiles.add(currentUrlSuffix); addOrUpdateFileUrls.put(currentFileUrl + currentUrlSuffix, diffFilePath + currentUrlSuffix); } } long fileAddOrUpdateend = System.currentTimeMillis(); String fileAddOrUpdate = "-------- 运行完毕,耗时:" + (fileAddOrUpdateend - fileAddOrUpdatestart) + "ms"; // 3.3写入日志文件 String addAndUpdateCurrentTime = createCurrentTime(); String addAndUpdateDiffMessage = "-------- Diff时间:" + addAndUpdateCurrentTime + " --------"; String updateCountMessage = "-------- 共更新文件" + updateCount + "个 -------"; String addCountMessage = "-------- 共新增文件" + addCount + "个 -------"; try { FileUtils.writeStringToFile(updateAndAddLogFile, addAndUpdateDiffMessage + "\n", "UTF-8", true); FileUtils.writeStringToFile(updateAndAddLogFile, updateCountMessage + "\n", "UTF-8", true); FileUtils.writeLines(updateAndAddLogFile, "UTF-8", updateFiles, true); FileUtils.writeStringToFile(updateAndAddLogFile, addCountMessage + "\n", "UTF-8", true); FileUtils.writeLines(updateAndAddLogFile, "UTF-8", addFiles, true); FileUtils.writeStringToFile(updateAndAddLogFile, fileAddOrUpdate + "\n", "UTF-8", true); } catch (IOException e1) { e1.printStackTrace(); } // 4.将有新增/修改的文件放入第三个目录中 for (Map.Entry<String, String> addOrUpdateEntry : addOrUpdateFileUrls.entrySet()) { String filePath = addOrUpdateEntry.getValue(); File diffFile = new File(addOrUpdateEntry.getValue()); String fileDirs = filePath.replace(diffFile.getName(), ""); File creFileDir = new File(fileDirs); creFileDir.mkdirs(); FileChannel inputChannel = null; FileChannel outputChannel = null; FileInputStream fileInputStream = null; FileOutputStream fileOutputStream = null; try { fileInputStream = new FileInputStream(new File(addOrUpdateEntry.getKey())); inputChannel = fileInputStream.getChannel(); fileOutputStream = new FileOutputStream(diffFile); outputChannel = fileOutputStream.getChannel(); outputChannel.transferFrom(inputChannel, 0, inputChannel.size()); } catch (Exception e) { e.printStackTrace(); } finally { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } try { inputChannel.close(); } catch (IOException e) { e.printStackTrace(); } try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } try { outputChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 获取yyyy年MM月dd日HH点mm分ss秒格式的当前时间 * * @return */ private static String createCurrentTime() { Date now = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日HH点mm分ss秒"); String nowTime = dateFormat.format(now); return nowTime; } /** * 将获取到的文件存到map中 * * @param path * @return */ private static HashMap<String, Long> getAllFileMap(String DirectoryUrl) { List<File> allFileList = new ArrayList<File>(); HashMap<String, Long> resMap = new HashMap<String, Long>(); allFileList = getAllFile(new File(DirectoryUrl), allFileList); for (File file : allFileList) { resMap.put(file.getAbsolutePath().replace(DirectoryUrl, ""), getFileCRC(file)); } return resMap; } /** * 递归遍历所有文件 * * @param file * @param allFileList * @return */ private static List<File> getAllFile(File file, List<File> allFileList) { if (file.exists()) { if (file.isDirectory()) { File f[] = file.listFiles(); for (File tempFile : f) { getAllFile(tempFile, allFileList); } } else { allFileList.add(file); } } return allFileList; } /** * 获取文件的CRC * * @param file * @return * @throws IOException */ 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) { } finally { if (bsrc != null) { try { bsrc.close(); } catch (IOException e) { e.printStackTrace(); } } } return crc.getValue(); } }
测试代码如下:
package comcollection.test; import org.junit.Test; /** * @author MJC *2018年4月25日 * 上午8:55:26 */ public class DiffUtilsTest { private static final String oldUrl = "C:\\Users\\jljd\\Desktop\\diff工具需求说明及示例\\diff工具需求说明及示例\\A"; private static final String newUrl = "C:\\Users\\jljd\\Desktop\\diff工具需求说明及示例\\diff工具需求说明及示例\\B"; private static final String diffUrl = "C:\\Users\\jljd\\Desktop\\diff工具需求说明及示例\\diff工具需求说明及示例\\change"; @Test public void scanFileDirectory(){ DiffUtils.scanFileDirectory(oldUrl, newUrl, diffUrl); } }
纸上学来终觉浅,觉知此事需躬行