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;
    }

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值