Java实现文件夹内容增量同步并且保证同步文件夹的强一致性

1.基本需求
  • 源目录有的文件,目标目录必须有
  • 源目录没有的文件,但目标目录有,称为非法文件,需要执行删除
  • 文件删除后,反向递归执行空目录删除
2.扩展需求
  • 正向递归删除空目录,弥补代码缺陷
3.代码实现
package www.cuit.edu.cn.other;


import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: 蒸只小鱼
 * @date: 2024/9/16 20:18
 * @description: 增量文件迁移与非法文件删除,确保源目录和目标目录的强一致性(已经存在的空目录需要额外写递归删除)
 */
public class IncrementalFileMigration {

    // 源目录前缀(默认自身)
    public static String sourcePathPrefix = null;
    // 目标目录前缀(默认自身)
    public static String targetPathPrefix = null;

    public static void main(String[] args) {
        // 迁移
//        String sourcePath = "E:\\桌面\\JavaNote-rewrite";
//        sourcePathPrefix = sourcePath;
//        String targetPath = "G:\\JavaNote-rewrite";
//        targetPathPrefix = targetPath;
//        incrementalFileMigration(sourcePath, targetPath);

        // 迁移测试
//        String sourcePath = "E:\\桌面\\source\\Java-Note";
//        sourcePathPrefix = sourcePath;
//        String targetPath = "E:\\桌面\\target\\Java-Note";
//        targetPathPrefix = targetPath;
//        incrementalFileMigration(sourcePath, targetPath);

        // 空目录正向递归删除
//        deleteEmptyDirForward("G:\\JavaNote-rewrite","G:\\JavaNote-rewrite");

    }

    /**
     * 增量文件迁移
     * @param sourcePath 源目录
     * @param targetPath 目标目录
     */
    public static void incrementalFileMigration(String sourcePath, String targetPath) {
        // 1.校验并获取源目录中所有的文件
        File sourceFile = new File(sourcePath);
        if (!sourceFile.exists()) {
            return;
        }
        List<String> sourceFiles = getAllFiles(sourcePath);
        sourceFiles = sourceFiles.parallelStream().map(s -> s.replace(sourcePathPrefix, "")).collect(Collectors.toList());
        // 2.校验并获取目标目录中所有的文件
        File targetFile = new File(targetPath);
        if (!targetFile.exists()) {
            // 2.1 创建目标目录
            if (targetFile.mkdirs()) {
                System.out.println("创建目标目录成功");
            } else {
                System.out.println("创建目标目录失败");
                return;
            }
        }
        List<String> targetFiles = getAllFiles(targetPath);
        targetFiles = targetFiles.parallelStream().map(s -> s.replace(targetPathPrefix, "")).collect(Collectors.toList());
        // 3.目标:源目录中的文件,目标目录必须有,源目录中没有的文件,目标目录必须没有
        //     为了提升性能,这里先把二者交集求出来
        Collection<String> intersection = new ArrayList<>();
        for (String file : targetFiles) {
            if (sourceFiles.contains(file)) {
                intersection.add(file);
            }
        }
        //     3.1 差集 --> sourceFiles - targetFiles : 源目录中有但目标目录中没有的文件 --> 拷贝
        Collection<String> waitCopyFiles = new ArrayList<>();
        for (String file : sourceFiles) {
            if (!intersection.contains(file))
                waitCopyFiles.add(file);
        }
        //     3.2 差集 --> targetFiles - sourceFiles : 目标目录中有但源目录中没有的文件 --> 删除
        Collection<String> waitDeleteFiles = new ArrayList<>();
        for (String file : targetFiles) {
            if (!intersection.contains(file)) {
                waitDeleteFiles.add(file);
            }
        }
        doCopy(waitCopyFiles);
        doDelete(waitDeleteFiles);
    }

    /**
     * 删除操作
     * @param waitDeleteFiles 待删除文件路径集合
     */
    private static void doDelete(Collection<String> waitDeleteFiles) {
        waitDeleteFiles.parallelStream().forEach(relativePath -> {
            File targetAbsolutePath = new File(targetPathPrefix, relativePath);
            boolean isDelete = targetAbsolutePath.delete();
            if (isDelete) {
                System.out.println("删除成功!");
                // 递归检查空目录并删除(如果这个过程到达了指定不允许删除的目录也要停止)
                deleteEmptyDir(targetAbsolutePath.getParentFile().getPath(), new File(targetPathPrefix).getPath());
            } else {
                System.out.println("删除失败!");
            }
        });
    }


    /**
     * 递归删除空目录
     * @param path 等待检验是否要删除的目录
     * @param stopPath 如果删除过程没有主动停止,则由终止目录结束,默认目标目录前缀
     */
    private static void deleteEmptyDir(String path, String stopPath) {
        File file = new File(path);
        File[] files = file.listFiles();
        if (path.equals(stopPath) || !file.exists() || (files != null && files.length != 0)) {
            return;
        }
        boolean isDelete = file.delete();
        if (isDelete) {
            deleteEmptyDir(file.getParentFile().getPath(), stopPath);
        }
    }

    /**
     * 拷贝操作
     * @param waitCopyFiles 待拷贝文件路径集合
     */
    private static void doCopy(Collection<String> waitCopyFiles) {
        waitCopyFiles.parallelStream().forEach(relativePath -> {
            File originAbsolutePath = new File(sourcePathPrefix, relativePath);
            File targetAbsolutePath = new File(targetPathPrefix, relativePath);
            File targetParentFile = targetAbsolutePath.getParentFile();
            if (!targetParentFile.exists()) {
                if (targetParentFile.mkdirs()) {
                    System.out.println("创建目标目录成功");
                } else if (targetParentFile.exists()) {
                    System.out.println("目录已由其他线程创建!");
                }
            }
            try {
                Files.copy(Paths.get(originAbsolutePath.getPath()), Paths.get(targetAbsolutePath.getPath()), StandardCopyOption.REPLACE_EXISTING);
            } catch (IOException e) {
                System.out.println("拷贝文件失败!");
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 获取指定路径下的所有文件
     * @param path 路径
     * @return 路径集合
     */
    private static List<String> getAllFiles(String path) {
        // 结果集
        List<String> paths = new ArrayList<>();
        // 1.file是文件
        File file = new File(path);
        if (file.isFile()) {
            paths.add(path);
        }
        // 2.file是目录
        if (file.isDirectory()) {
            // 2.1 拿到这个目录下的所有文件或目录,分别对每个目录执行获取获取其下所有文件的操作
            for (File f : file.listFiles()) {
                paths.addAll(getAllFiles(f.getPath()));
            }
        }
        return paths;
    }

    /**
     * 正向递归删除空目录
     * @param path 目标目录
     * @param stopPath 不允许删除的目录
     */
    public static void deleteEmptyDirForward(String path,String stopPath) {
        File file = new File(path);
        if(!file.exists()){
            return;
        }
        File[] files = file.listFiles();
        // 空目录
        if ((files == null || (files != null && files.length == 0)) && !path.equals(stopPath)) {
            boolean isDelete = file.delete();
            if (isDelete) {
                System.out.println("删除成功");
            } else {
                System.out.println("删除失败");
            }
        }
        // 非空
        for (File f : files) {
            if (f.isDirectory()) {
                deleteEmptyDirForward(f.getPath(),stopPath);
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值