Java实现文件目录的DIFF

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


package com.kang.test;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
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.log4j.Logger;

public class DiffUtil {

    public static void scanFileDirectory(File baseFile, File afterFile,
            File diffFileUrl, Logger logger) {

        // 在diffUrl目录下,创建日期格式的文件夹,用于存放改动的文件
        // 获取当前日期
        String nowTime = createCurrentTime();

        // 创建日期形式的文件目录
        File fileDire = new File(diffFileUrl, nowTime);
        fileDire.mkdirs();
        String diffFilePath = fileDire.getAbsolutePath();

        // 遍历出删除的信息,并记录到删除日志文件中
        // 创建删除日志文件
        String rootPath = fileDire.getAbsolutePath();
        File deleteLogFile = new File(rootPath, "delete.log");
        try {
            deleteLogFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 遍历出删除的信息
        HashMap<String, Long> sourceUrlMap = getAllFileMap(baseFile);
        HashMap<String, Long> currentUrlMap = getAllFileMap(afterFile);
        List<String> deleteFiles = new ArrayList<String>();

        long start = System.currentTimeMillis();
        logger.info(DiffUtilsTest.class.getName() + "开始Diff,参数1:" + baseFile
                + "参数2:" + afterFile + "参数3:" + diffFileUrl);

        int delFilecount = 0;
        for (Map.Entry<String, Long> sourceEntry : sourceUrlMap.entrySet()) {
            // 遍历原版本目录下的文件,如果该文件不在当前版本目录下面,即该文件被删除了
            if (!currentUrlMap.containsKey(sourceEntry.getKey())) {
                deleteFiles.add(sourceEntry.getKey());
                logger.info("文件被删除:" + sourceEntry.getKey());
                delFilecount++;
            }
        }
        // 把删除信息写入log文件
        appendFileWriter(deleteLogFile, deleteFiles);

        // 遍历新增与更新记录,并存入到集合中
        List<String> addFiles = new ArrayList<String>();
        List<String> updateFiles = new ArrayList<String>();

        // 创建修改或者新增文件
        File addOrUpdate = new File(rootPath, "updateAndAdd.log");
        try {
            addOrUpdate.createNewFile();
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        /**
         * addOrUpdateFileUrls的key-value分别为afterFile文件的全路径名、 diffFileUrl文件的全路径名
         * 通过文件的完全路径可以获得文件对象,用于后面的文件复制操作
         */
        Map<String, String> addOrUpdateFileUrls = new HashMap<String, String>();
        int addCount = 0;
        int updateCount = 0;
        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);
                        logger.info("文件发生改变:" + currentUrlSuffix);
                        addOrUpdateFileUrls.put(afterFile + currentUrlSuffix,
                                diffFilePath + currentUrlSuffix);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            } else {
                // 如果元素不存在A目录下,就是新增的元素
                addCount++;
                addFiles.add(currentUrlSuffix);
                addOrUpdateFileUrls.put(afterFile + currentUrlSuffix,
                        diffFilePath + currentUrlSuffix);
                logger.info("新增文件:" + currentUrlSuffix);
            }
        }
        // 把修改或者新增文件写入log文件
        // log信息补充
        addFiles.add(0, "-------- 共新增文件" + addCount + "个 --------");
        updateFiles.add(0, "-------- 共更新文件" + updateCount + "个 --------");
        addFiles.addAll(updateFiles);
        long end = System.currentTimeMillis();

        addFiles.add("--------  运行完毕,耗时:" + (end - start) + "ms");
        appendFileWriter(addOrUpdate, addFiles);

        // 总体变化情况
        logger.info("-------- 共更新文件:" + updateCount + "个" + "新增:" + addCount
                + "个" + "删除:" + delFilecount + "个");

        // 将有新增/修改的文件放入change目录中
        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();
                    inputChannel.close();
                    fileOutputStream.close();
                    outputChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 把List中的信息写入对应的log文件
     * 
     * @param file
     * @param value
     * @return
     */
    private static void appendFileWriter(File file, List<String> value) {
        // 打开一个文件写入器,以追加的形式写入
        try {
            FileWriter writer = new FileWriter(file, true);
            for (String fileName : value) {
                writer.write(fileName);
                writer.write("\r\n");
            }
            writer.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }



    /**
     * 将获取到的文件存到map中
     * 
     * @param path
     * @return
     */
    private static HashMap<String, Long> getAllFileMap(File DirectoryUrl) {
        // 暂存所有文件
        List<File> allFileList = new ArrayList<File>();

        // 利用Map存文件的相对路径和CRC信息
        HashMap<String, Long> resMap = new HashMap<String, Long>();

        allFileList = getAllFile(DirectoryUrl, allFileList);
        for (File file : allFileList) {
            // key-value:文件的相对路径-文件的CRC信息
            resMap.put(
                    file.getAbsolutePath().replace(
                            DirectoryUrl.getAbsolutePath(), ""),
                    getFileCRC(file));
        }
        return resMap;
    }

    /**
     * 递归遍历所有文件
     * 
     * @param file
     * @param allFileList
     * @return
     */
    private static List<File> getAllFile(File file, List<File> allFileList) {
        // 判断文件是否存在
        if (file.exists()) {
            // 判断是否是目录:是,获取所有文件按;否存入List;
            if (file.isDirectory()) {
                File f[] = file.listFiles();
                for (File tempFile : f) {
                    getAllFile(tempFile, allFileList);
                }
            } else {
                allFileList.add(file);
            }
        }
        return allFileList;
    }

高人指点后,第二个版本
文件的COPY部分

        // 将有新增/修改的文件放入change目录中
        for (Map.Entry<String, String> addOrUpdateEntry : addOrUpdateFileUrls
                .entrySet()) {
            try {
                FileUtils.copyFile(new File(addOrUpdateEntry.getKey()),
                        new File(addOrUpdateEntry.getValue()));
            } catch (IOException e) {
                logger.error(Const.ERRORFILEWRITE, e);
            }
        }

文件的遍历存储部分

/**
     * 递归遍历所有文件
     * 
     * @param file
     *            目标目录
     * @param allFileList
     * @return HashMap<String, Long> 文件列表
     * @throws IOException
     */
    private static HashMap<String, Long> getAllFileMap(File file,
            String filePath, HashMap<String, Long> resMap) throws IOException {
        // 判断文件是否存在
        if (file.exists()) {
            // 判断文件是不是目录:是,获取子目录;不是,存入list;
            if (file.isDirectory()) {
                File f[] = file.listFiles();
                for (File tempFile : f) {
                    getAllFileMap(tempFile, filePath, resMap);
                }
            } else {
                resMap.put(file.getAbsolutePath().replace(filePath, ""),
                        FileUtils.checksumCRC32(file));
            }
        }
        return resMap;
    }

本来两次的遍历没有考虑内存性能等。相比较下,后续优化了很多。

Git diff算法是一种比较两个文件或者两个版本之间差异的算法,Java中可以通过实现这个算法来实现git diff功能。下面是Java实现git diff算法的步骤: 1.将两个文件分别读入内存中,并将其转换为字符串。 2.将两个字符串按照行进行分割,并将每一行的内容存储到一个数组中。 3.使用动态规划算法,计算出两个字符串之间的最长公共子序列。 4.根据最长公共子序列,生成差异报告。 5.将差异报告输出到控制台或者文件中。 下面是Java实现git diff算法的代码示例: ```java import java.util.ArrayList; import java.util.List; public class GitDiff { public static void main(String[] args) { String str1 = "xxxxxxxxxx"; String str2 = "yyyyyyyyyy"; List<String> list1 = splitString(str1); List<String> list2 = splitString(str2); int[][] dp = new int[list1.size() + 1][list2.size() + 1]; for (int i = 1; i <= list1.size(); i++) { for (int j = 1; j <= list2.size(); j++) { if (list1.get(i - 1).equals(list2.get(j - 1))) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } List<String> result = new ArrayList<>(); int i = list1.size(); int j = list2.size(); while (i > 0 && j > 0) { if (list1.get(i - 1).equals(list2.get(j - 1))) { i--; j--; } else if (dp[i - 1][j] >= dp[i][j - 1]) { result.add("delete " + list1.get(i - 1)); i--; } else { result.add("add " + list2.get(j - 1)); j--; } } while (i > 0) { result.add("delete " + list1.get(i - 1)); i--; } while (j > 0) { result.add("add " + list2.get(j - 1)); j--; } for (int k = result.size() - 1; k >= 0; k--) { System.out.println(result.get(k)); } } private static List<String> splitString(String str) { List<String> list = new ArrayList<>(); String[] arr = str.split("\n"); for (String s : arr) { list.add(s); } return list; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值