Skip to content

File Synchronization Tool

Abstract

File Synchronization Tool is a Python utility that keeps files in two directories synchronized. It demonstrates file system operations, hashing, and automation. This project is great for learning about file management, automation, and error handling in Python.

Prerequisites

  • Python 3.6 or above
  • os, shutil, hashlib (built-in)

Before you Start

Make sure you have Python installed. No external packages are required; all modules used are built-in.

Getting Started

  1. Create a folder named file-sync-toolfile-sync-tool.
  2. Create a file named file_synchronization_tool.pyfile_synchronization_tool.py.
  3. Copy the code below into your file.
⚙️ File Synchronization Tool
File Synchronization Tool
"""
File Synchronization Tool
 
A tool to synchronize files between two directories with the following features:
- Detect and copy new or updated files
- Delete files that no longer exist in the source directory
- Provide a summary of synchronization actions
"""
 
import os
import shutil
from tkinter import Tk, Label, Entry, Button, messagebox, filedialog
 
 
class FileSynchronizationTool:
    def __init__(self, root):
        self.root = root
        self.root.title("File Synchronization Tool")
 
        self.source_dir = ""
        self.target_dir = ""
 
        self.setup_ui()
 
    def setup_ui(self):
        """Set up the user interface."""
        Label(self.root, text="Source Directory:").grid(row=0, column=0, padx=10, pady=10)
        self.source_entry = Entry(self.root, width=50)
        self.source_entry.grid(row=0, column=1, padx=10, pady=10)
        Button(self.root, text="Browse", command=self.browse_source).grid(row=0, column=2, padx=10, pady=10)
 
        Label(self.root, text="Target Directory:").grid(row=1, column=0, padx=10, pady=10)
        self.target_entry = Entry(self.root, width=50)
        self.target_entry.grid(row=1, column=1, padx=10, pady=10)
        Button(self.root, text="Browse", command=self.browse_target).grid(row=1, column=2, padx=10, pady=10)
 
        Button(self.root, text="Synchronize", command=self.synchronize).grid(row=2, column=0, columnspan=3, pady=20)
 
    def browse_source(self):
        """Browse for the source directory."""
        self.source_dir = filedialog.askdirectory()
        self.source_entry.delete(0, "end")
        self.source_entry.insert(0, self.source_dir)
 
    def browse_target(self):
        """Browse for the target directory."""
        self.target_dir = filedialog.askdirectory()
        self.target_entry.delete(0, "end")
        self.target_entry.insert(0, self.target_dir)
 
    def synchronize(self):
        """Synchronize files between the source and target directories."""
        if not self.source_dir or not self.target_dir:
            messagebox.showerror("Error", "Both source and target directories must be selected.")
            return
 
        if not os.path.exists(self.source_dir):
            messagebox.showerror("Error", "Source directory does not exist.")
            return
 
        if not os.path.exists(self.target_dir):
            os.makedirs(self.target_dir)
 
        actions = []
 
        # Copy new and updated files
        for root, dirs, files in os.walk(self.source_dir):
            relative_path = os.path.relpath(root, self.source_dir)
            target_root = os.path.join(self.target_dir, relative_path)
 
            if not os.path.exists(target_root):
                os.makedirs(target_root)
 
            for file in files:
                source_file = os.path.join(root, file)
                target_file = os.path.join(target_root, file)
 
                if not os.path.exists(target_file) or os.path.getmtime(source_file) > os.path.getmtime(target_file):
                    shutil.copy2(source_file, target_file)
                    actions.append(f"Copied: {source_file} -> {target_file}")
 
        # Delete files that no longer exist in the source directory
        for root, dirs, files in os.walk(self.target_dir):
            relative_path = os.path.relpath(root, self.target_dir)
            source_root = os.path.join(self.source_dir, relative_path)
 
            for file in files:
                target_file = os.path.join(root, file)
                source_file = os.path.join(source_root, file)
 
                if not os.path.exists(source_file):
                    os.remove(target_file)
                    actions.append(f"Deleted: {target_file}")
 
        # Show summary
        if actions:
            messagebox.showinfo("Synchronization Complete", "\n".join(actions))
        else:
            messagebox.showinfo("Synchronization Complete", "No changes were made.")
 
 
def main():
    root = Tk()
    app = FileSynchronizationTool(root)
    root.mainloop()
 
 
if __name__ == "__main__":
    main()
 
File Synchronization Tool
"""
File Synchronization Tool
 
A tool to synchronize files between two directories with the following features:
- Detect and copy new or updated files
- Delete files that no longer exist in the source directory
- Provide a summary of synchronization actions
"""
 
import os
import shutil
from tkinter import Tk, Label, Entry, Button, messagebox, filedialog
 
 
class FileSynchronizationTool:
    def __init__(self, root):
        self.root = root
        self.root.title("File Synchronization Tool")
 
        self.source_dir = ""
        self.target_dir = ""
 
        self.setup_ui()
 
    def setup_ui(self):
        """Set up the user interface."""
        Label(self.root, text="Source Directory:").grid(row=0, column=0, padx=10, pady=10)
        self.source_entry = Entry(self.root, width=50)
        self.source_entry.grid(row=0, column=1, padx=10, pady=10)
        Button(self.root, text="Browse", command=self.browse_source).grid(row=0, column=2, padx=10, pady=10)
 
        Label(self.root, text="Target Directory:").grid(row=1, column=0, padx=10, pady=10)
        self.target_entry = Entry(self.root, width=50)
        self.target_entry.grid(row=1, column=1, padx=10, pady=10)
        Button(self.root, text="Browse", command=self.browse_target).grid(row=1, column=2, padx=10, pady=10)
 
        Button(self.root, text="Synchronize", command=self.synchronize).grid(row=2, column=0, columnspan=3, pady=20)
 
    def browse_source(self):
        """Browse for the source directory."""
        self.source_dir = filedialog.askdirectory()
        self.source_entry.delete(0, "end")
        self.source_entry.insert(0, self.source_dir)
 
    def browse_target(self):
        """Browse for the target directory."""
        self.target_dir = filedialog.askdirectory()
        self.target_entry.delete(0, "end")
        self.target_entry.insert(0, self.target_dir)
 
    def synchronize(self):
        """Synchronize files between the source and target directories."""
        if not self.source_dir or not self.target_dir:
            messagebox.showerror("Error", "Both source and target directories must be selected.")
            return
 
        if not os.path.exists(self.source_dir):
            messagebox.showerror("Error", "Source directory does not exist.")
            return
 
        if not os.path.exists(self.target_dir):
            os.makedirs(self.target_dir)
 
        actions = []
 
        # Copy new and updated files
        for root, dirs, files in os.walk(self.source_dir):
            relative_path = os.path.relpath(root, self.source_dir)
            target_root = os.path.join(self.target_dir, relative_path)
 
            if not os.path.exists(target_root):
                os.makedirs(target_root)
 
            for file in files:
                source_file = os.path.join(root, file)
                target_file = os.path.join(target_root, file)
 
                if not os.path.exists(target_file) or os.path.getmtime(source_file) > os.path.getmtime(target_file):
                    shutil.copy2(source_file, target_file)
                    actions.append(f"Copied: {source_file} -> {target_file}")
 
        # Delete files that no longer exist in the source directory
        for root, dirs, files in os.walk(self.target_dir):
            relative_path = os.path.relpath(root, self.target_dir)
            source_root = os.path.join(self.source_dir, relative_path)
 
            for file in files:
                target_file = os.path.join(root, file)
                source_file = os.path.join(source_root, file)
 
                if not os.path.exists(source_file):
                    os.remove(target_file)
                    actions.append(f"Deleted: {target_file}")
 
        # Show summary
        if actions:
            messagebox.showinfo("Synchronization Complete", "\n".join(actions))
        else:
            messagebox.showinfo("Synchronization Complete", "No changes were made.")
 
 
def main():
    root = Tk()
    app = FileSynchronizationTool(root)
    root.mainloop()
 
 
if __name__ == "__main__":
    main()
 
  1. Run the script: python file_synchronization_tool.pypython file_synchronization_tool.py

Explanation

Code Breakdown

  1. Import modules
import os
import shutil
import hashlib
import os
import shutil
import hashlib
  1. Compare and sync files
def sync_dirs(src, dest):
    for filename in os.listdir(src):
        src_file = os.path.join(src, filename)
        dest_file = os.path.join(dest, filename)
        if not os.path.exists(dest_file) or hash_file(src_file) != hash_file(dest_file):
            shutil.copy2(src_file, dest_file)
def sync_dirs(src, dest):
    for filename in os.listdir(src):
        src_file = os.path.join(src, filename)
        dest_file = os.path.join(dest, filename)
        if not os.path.exists(dest_file) or hash_file(src_file) != hash_file(dest_file):
            shutil.copy2(src_file, dest_file)
  1. Hashing function
def hash_file(path):
    with open(path, 'rb') as f:
        return hashlib.md5(f.read()).hexdigest()
def hash_file(path):
    with open(path, 'rb') as f:
        return hashlib.md5(f.read()).hexdigest()

Features

  • Syncs files between folders
  • Uses hashing for change detection
  • Automates backup tasks
  • Simple CLI usage

How It Works

  • Compares files by hash
  • Copies changed/new files
  • Ensures destination matches source

Use Cases

  • Backup important files
  • Mirror folders
  • Automate file management
  • Keep USB drives synced

Next Steps

You can enhance this project by:

  • Adding logging
  • Supporting subdirectories
  • Scheduling syncs
  • Adding a GUI
  • Handling file deletions
  • Conflict resolution

Enhanced Version Ideas

def sync_with_logging(src, dest):
    # Log all sync actions
    pass
 
def sync_subdirs(src, dest):
    # Recursively sync folders
    pass
def sync_with_logging(src, dest):
    # Log all sync actions
    pass
 
def sync_subdirs(src, dest):
    # Recursively sync folders
    pass

Troubleshooting Tips

  • Permission errors: Check file access
  • Hash mismatch: Re-run sync
  • File not copied: Check source/destination paths

Conclusion

This project teaches file operations, hashing, and automation. Expand it for more robust syncing and scheduling. Useful for backups and file management.

Was this page helpful?

Let us know how we did