Java实现两个文件夹之间的同步(改进版)

这里说的改进是相对于第一版https://blog.csdn.net/qq_46037812/article/details/120163996来说的

写这个小工具的目的是方便我在电脑上进行把webdav挂载的云盘中的笔记与本地笔记同步、把平时拍的照片进行多端同步的操作,其实有很多现成的软件可以完美地完成这件事,比如Microsoft SyncToy之类的,不过这是在我做完第一版之后才知道的,所以干脆一不做二不休,再完善一下自己做的小工具,让它更好地胜任我平时需要的文件同步工作

注意:此工具未经过全面的测试,可能存在一些bug,不建议用在重要文件之上。

对比上个版本,有以下改进

  1. 支持空文件夹的同步、无效文件夹的删除
  2. 另起线程进行费时的查找、同步操作,避免堵塞swing线程
  3. 同步操作有可视化的进度百分比,当同步的文件数量很多或者同步的目标文件夹是在本地挂载的云盘中,导致上传文件受网速限制时,这个进度百分比还是挺有用的。

目前处于学习阶段,代码改得比较乱,可能存在很多不必要的操作影响性能,欢迎大家指出不足之处。


源码

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class File_Syn
{
    static Map<String, File> fileMap = new HashMap<>();
    static Map<String, File> folderMap = new HashMap<>();
    static Map<String, File> fileMap1 = new HashMap<>();
    static Map<String, File> fileMap2 = new HashMap<>();
    static Map<String, File> folderMap1 = new HashMap<>();
    static Map<String, File> folderMap2 = new HashMap<>();
    static Map<String, File> deleteFileMap = new HashMap<>();
    static Map<String, File> nullFolder = new HashMap<>();
    static int count = 0;
    public static void main(String[] args) { new Form();}

    /**
     *
     * @param file 源文件夹
     * @param fileMap 文件夹中的文件Map
     * @param sourceFilePath 源文件夹路径
     * @return Map
     */
    public static Map<String, File> getFileMap(File file, Map<String, File> fileMap, Map<String, File> folderMap,String sourceFilePath)
    {
        for (int i = 0; i < file.listFiles().length; i++)
        {
            File childFile = file.listFiles()[i];
            if (!childFile.isDirectory()) //如果不是文件夹
            {
                String childPath = childFile.getAbsolutePath(); //获取绝对路径
                String relativePath = childPath.substring(sourceFilePath.length());//从绝对路径中取得相对路径
                fileMap.put(relativePath,childFile);
            }
            else
            {
                folderMap.put(childFile.getAbsolutePath().substring(sourceFilePath.length()),childFile);  //获取文件夹结构
                getFileMap(childFile, fileMap, folderMap,sourceFilePath);//递归查询
            }
        }
        return fileMap;
    }

    /**
     * 查询两个文件夹中的差异文件个数,将差异文件路径存储在fileMap中
     * @param sourceFile 源文件夹
     * @param destinationFile 目标文件夹
     */
    public static void search(File sourceFile, File destinationFile)
    {
        fileMap1 = getFileMap(sourceFile, fileMap1, folderMap1,sourceFile.getAbsolutePath());
        fileMap2 = getFileMap(destinationFile, fileMap2, folderMap2,destinationFile.getAbsolutePath());

        for (Map.Entry<String, File> entry : fileMap1.entrySet()) //遍历源文件夹的文件
        {
            String key = entry.getKey();
            if (fileMap2.containsKey(key)) //如果目标文件夹中存在此文件
            {
                Date date1 = new Date(entry.getValue().lastModified());
                Date date2 = new Date(fileMap2.get(key).lastModified());
                if (date1.after(date2)) //对比最后修改时间,如果源文件夹中文件较新,则更新
                {
                    fileMap.put(key, entry.getValue());
                }
            }
            else
            {
                fileMap.put(key, entry.getValue());
            }
        }
        for (Map.Entry<String, File> entry : folderMap1.entrySet())  //遍历文件夹
        {
            String key = entry.getKey();
            if (!folderMap2.containsKey(key))
            {
                folderMap.put(key,entry.getValue());
            }
        }
        for (Map.Entry<String, File> entry : fileMap2.entrySet()) //需要删除的差异
        {
            String key = entry.getKey();
            if (!fileMap1.containsKey(key))
            {
                deleteFileMap.put(key,entry.getValue());
            }
        }
        for (Map.Entry<String, File> entry : folderMap2.entrySet()) //无效文件夹
        {
            String key = entry.getKey();
            if (!folderMap1.containsKey(key))
            {
                nullFolder.put(key,entry.getValue());
            }
        }
    }

    /**
     *
     * @param destinationFile 目标文件夹
     * @param jTextArea 文本域
     */
    public static void change(File destinationFile,JTextArea jTextArea)
    {
        String destinationFilePath = destinationFile.getAbsolutePath();
        for (Map.Entry<String, File> entry : folderMap.entrySet())  //建立文件夹结构
        {
            String key = entry.getKey();
            File file = new File(destinationFilePath + key);
            file.mkdirs();

        }
        for (Map.Entry<String, File> entry : fileMap.entrySet())  //同步文件
        {
            String key = entry.getKey();
            File file1 = entry.getValue();
            File file2 = new File(destinationFilePath + key);
            if (fileMap2.containsKey(key))
            {
                file2 = fileMap2.get(key);
                file2.delete(); //删除已存在的文件
            }
            try {
                Files.copy(file1.toPath(), file2.toPath()); //文件复制
                count++;
            }catch (IOException e) {
                e.printStackTrace();
                jTextArea.setText("同步失败");
            }
        }
        for (Map.Entry<String, File> entry : deleteFileMap.entrySet())  //删除无效文件
        {
            File deleteFile = entry.getValue();
            if (deleteFile.exists())
            {
                deleteFile.delete();
            }
            count++;
        }
        for (Map.Entry<String, File> entry : nullFolder.entrySet())  //删除无效文件夹
        {
            File deleteFolder = entry.getValue();
            if (deleteFolder.exists())
                deleteFolder(deleteFolder);
        }
        jTextArea.setText("同步完成");
    }
    public static void deleteFolder(File file)
    {
        File[] files = file.listFiles();
        if (files.length != 0)
        {
            for (int i = 0; i < files.length; i++)
                deleteFolder(files[i]);
        }
        file.delete();
        count++;
    }
    public static void clear()
    {
        folderMap.clear();fileMap.clear();
        fileMap1.clear();fileMap2.clear();
        folderMap1.clear();folderMap2.clear();
        deleteFileMap.clear();nullFolder.clear();
        count = 0;
    }
}
class Form extends JFrame
{
    Font font = new Font("宋体",0,20);
    JButton sourceBtn = new JButton("选择源文件夹");
    JButton destinationBtn = new JButton("选择目标文件夹");
    JButton compare = new JButton("对比文件夹");
    JButton change = new JButton("同步");
    JTextArea jTextArea1 = new JTextArea(2,25);
    JTextArea jTextArea2 = new JTextArea(2,25);
    JTextArea result = new JTextArea(3,25);
    JPanel jPanel1 = new JPanel();
    JPanel jPanel2 = new JPanel();
    JPanel jPanel3 = new JPanel();
    JPanel jPanel4 = new JPanel();
    JPanel jPanel5 = new JPanel();
    JFileChooser jFileChooser = new JFileChooser();
    File sourceFilePath = null;
    File destinationFilePath = null;
    Form()
    {
        setSize(300,450);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new GridLayout(5,1));
        setLocationRelativeTo(null);
        setVisible(true);
        jTextArea1.setFont(font);jTextArea1.setLineWrap(true);jTextArea1.setWrapStyleWord(true);
        jTextArea2.setFont(font);jTextArea2.setLineWrap(true);jTextArea2.setWrapStyleWord(true);
        result.setFont(font);result.setLineWrap(true);result.setWrapStyleWord(true);
        jPanel1.add(sourceBtn);jPanel1.add(destinationBtn);
        jPanel2.add(jTextArea1);
        jPanel3.add(jTextArea2);
        jPanel4.add(compare);jPanel4.add(change);
        jPanel5.add(result);
        add(jPanel1);add(jPanel2);add(jPanel3);add(jPanel4);add(jPanel5);
        jFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        sourceBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int returnVal = jFileChooser.showOpenDialog(null);
                if (returnVal == JFileChooser.APPROVE_OPTION)
                {
                    sourceFilePath = jFileChooser.getSelectedFile();
                    jTextArea1.setText(sourceFilePath.getAbsolutePath());
                }
            }
        });
        destinationBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int returnVal = jFileChooser.showOpenDialog(null);
                if (returnVal == JFileChooser.APPROVE_OPTION)
                {
                    destinationFilePath  = jFileChooser.getSelectedFile();
                    jTextArea2.setText(destinationFilePath.getAbsolutePath());
                }
            }
        });
        compare.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (sourceFilePath == null || destinationFilePath == null)
                {
                    result.setText("未选择文件夹");
                    return;
                }
                if (sourceFilePath.equals(destinationFilePath))
                {
                    result.setText("源文件夹和目标文件夹不能相同");
                    return;
                }
                File_Syn.clear();
                result.setText("正在查找差异文件");
                new Thread(new t1()).start();
            }
        });
        change.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (File_Syn.fileMap.isEmpty() && File_Syn.deleteFileMap.isEmpty() && File_Syn.nullFolder.isEmpty())
                {
                    result.setText("文件差异列表为空");
                    return;
                }
                new Thread(new t2_per()).start();
                new Thread(new t2()).start();
            }
        });
    }
    class t1 implements Runnable
    {
        @Override
        public void run() {
            long start = System.currentTimeMillis();
            File_Syn.search(sourceFilePath,destinationFilePath);
            long finish = System.currentTimeMillis();
            result.setText("查找到"+(File_Syn.fileMap.size()+File_Syn.deleteFileMap.size()+File_Syn.nullFolder.size())+"处差异,其中"+File_Syn.nullFolder.size()+"个无效文件夹");
            if ((finish - start) > 1000)
                result.append("\n执行耗时:"+(finish-start)/1000+"秒");
            else
                result.append("\n执行耗时:"+(finish-start)+"毫秒");
        }
    }
    class t2 implements Runnable
    {
        @Override
        public void run() {
            long start = System.currentTimeMillis();
            File_Syn.change(destinationFilePath,result);
            long finish = System.currentTimeMillis();
            if ((finish - start) > 1000)
                result.append("\n执行耗时:"+(finish-start)/1000+"秒");
            else
                result.append("\n执行耗时:"+(finish-start)+"毫秒");
        }
    }
    class t2_per implements Runnable
    {
        @Override
        public void run() {
            BigDecimal percentage;
            int all = File_Syn.fileMap.size()+File_Syn.deleteFileMap.size()+File_Syn.nullFolder.size();
            BigDecimal bigAll = new BigDecimal(all);
            BigDecimal b100 = new BigDecimal(1);
            DecimalFormat decimalFormat = new DecimalFormat("0.00%");
            do {
                percentage = new BigDecimal(File_Syn.count).divide(bigAll,4, BigDecimal.ROUND_HALF_DOWN);
                if (percentage.compareTo(b100) == -1)
                    result.setText("正在处理,进度:"+decimalFormat.format(percentage));
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } while (percentage.compareTo(b100) == -1);
            if (bigAll.compareTo(new BigDecimal(File_Syn.count)) == 0)
                File_Syn.clear();
            else
                result.setText("同步存在异常,未完全完成");
        }
    }
}

运行截图 

 

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 在 Java 实现两个系统的部门同步功能可以使用以下步骤: 1. 定义两个系统的 API 接口,用于获取部门信息。 2. 创建 Java 程序,使用这两个系统的 API 接口获取部门信息。 3. 使用 Java 的对象序列化功能,将获取到的两个系统的部门信息序列化为字节数组。 4. 将两个系统的部门信息字节数组进行比较,如果不同则执行同步操作。 5. 同步操作可以使用两个系统的 API 接口进行部门信息的更新。 示例代码如下: ``` import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.List; public class DepartmentSync { private System1API system1API; private System2API system2API; public void sync() throws IOException, ClassNotFoundException { // 获取两个系统的部门信息 List<Department> system1Departments = system1API.getDepartments(); List<Department> system2Departments = system2API.getDepartments(); // 序列化两个系统的部门信息 byte[] system1Data = serialize(system1Departments); byte[] system2Data = serialize(system2Departments); // 比较两个系统的部门信息,如果不同则执行同步操作 if (!java.util.Arrays.equals(system1Data, system2Data)) { system2API.updateDepartments(system1Departments); } } private byte[] serialize(Object object) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject( ### 回答2: 使用JAVA实现两个系统的部门同步功能可以通过以下步骤: 1. 首先,需要在两个系统建立一个共享的数据库或者API接口用来存储和获取部门相关信息。可以使用MySQL或者其他关系型数据库来实现共享数据库,也可以使用RESTful API接口来实现数据的传输。 2. 在JAVA,可以使用JDBC连接到共享的数据库,通过执行SQL查询语句来获取部门信息。首先,在每个系统建立一个部门类,包含部门的ID、名称、描述等属性。然后,使用JDBC连接到数据库并执行对应的查询语句,将查询结果映射到对应的部门类。 3. 接下来,需要实现数据的同步功能。可以使用定时任务或者消息队列来实现数据的同步。定时任务可以每隔一段时间执行一次同步操作,而消息队列可以在一个系统创建一个消息,然后订阅者在另一个系统接收并处理这个消息。 4. 在同步时,可以通过判断数据库的部门表的变化来确定需要同步的数据。可以使用增量同步的方式,即只同步新增或者修改的数据,而不同步已经删除的数据。可以在每次同步时记录上次同步的时间点,在下次同步时只同步在这个时间点之后发生变化的数据。 5. 在处理同步数据时,可以通过判断部门在目标系统是否存在来决定是新增、修改还是删除这个部门。如果目标系统已经存在这个部门,则更新部门的相关属性;如果不存在这个部门,则新增这个部门;如果源系统的部门已经被删除,则在目标系统删除这个部门。 通过以上步骤,使用JAVA实现两个系统的部门同步功能就可以实现。可以根据实际需求和系统架构的不同进行适当的调整和优化。 ### 回答3: 要用JAVA实现两个系统的部门同步功能,可以按照以下步骤: 1. 连接两个系统:首先,需要建立两个系统之间的连接。可以使用JAVA提供的网络编程相关库来实现此功能,例如通过Socket建立一个TCP连接,或者使用HTTP协议进行通信。 2. 获取部门数据:在连接建立之后,需要从每个系统获取部门的数据。可以使用系统提供的API或者数据库查询来获取数据。根据两个系统的数据结构和接口设计,使用JAVA编写代码来获取每个系统的部门信息。 3. 数据同步:获取到两个系统的部门数据之后,需要对数据进行同步操作。首先,需要比较两个系统的部门数据是否一致,可以根据部门的唯一标识进行比较。如果两个系统的部门数据不一致,就根据同步策略进行相应的操作,例如更新、插入或删除数据。 4. 实现同步功能:根据数据同步的逻辑和策略,在JAVA编写代码来实现数据的同步操作。可以使用数据库操作语言来更新、插入或删除数据,或者使用系统提供的API来实现同步功能。 5. 异常处理:在进行数据同步操作时,可能会遇到一些异常情况,例如网络连接断、数据格式错误等。为了保证系统的稳定性,需要在JAVA代码添加异常处理机制,针对不同的异常情况进行相应的处理,例如进行重试、记录日志等。 总之,要用JAVA实现两个系统的部门同步功能,需要确保连接两个系统、获取数据、数据同步和异常处理等步骤都得到正确的实现。同时也要考虑代码的可读性和可维护性,使用合适的设计模式和编码规范,以便后续的代码维护和扩展。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值