再也不用在10个群里找文件了!

你有没有过这样的经历:

  • 下载文件夹里有几千个文件,找个东西要翻半天
  • 图片、文档、视频混在一起,乱成一锅粥
  • 想把照片按日期整理,一个一个移动累死

别再假装你会手动整理文件夹了,用Python吧!

今天我们做一个自动整理文件夹的工具,几秒钟就能把几千个文件分类整理好。

项目目标

实现一个自动整理文件夹的工具:

  1. 自动识别文件类型(图片、文档、视频、音乐等)
  2. 按类型把文件移动到对应的文件夹
  3. 支持自定义分类规则
  4. 支持预览将要执行的操作
  5. 可以回滚操作

完整代码

import os
import shutil
from datetime import datetime
import json

class FileOrganizer:
    """文件整理工具"""

    def __init__(self, folder_path):
        self.folder_path = folder_path
        self.operations = []  # 记录所有操作,用于回滚

        # 默认分类规则
        self.rules = {
            "图片": [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".webp"],
            "文档": [".txt", ".doc", ".docx", ".pdf", ".xls", ".xlsx", ".ppt", ".pptx"],
            "视频": [".mp4", ".avi", ".mov", ".mkv", ".flv", ".wmv"],
            "音乐": [".mp3", ".wav", ".flac", ".aac", ".ogg"],
            "压缩包": [".zip", ".rar", ".7z", ".tar", ".gz"],
            "代码": [".py", ".js", ".html", ".css", ".java", ".c", ".cpp"]
        }

        # 加载自定义规则
        self.load_rules()

    def load_rules(self):
        """加载自定义分类规则"""
        rules_file = os.path.join(self.folder_path, "organizer_rules.json")
        if os.path.exists(rules_file):
            try:
                with open(rules_file, "r", encoding="utf-8") as f:
                    self.rules = json.load(f)
                print("已加载自定义分类规则")
            except Exception as e:
                print(f"加载规则失败:{e}")

    def save_rules(self):
        """保存自定义分类规则"""
        rules_file = os.path.join(self.folder_path, "organizer_rules.json")
        try:
            with open(rules_file, "w", encoding="utf-8") as f:
                json.dump(self.rules, f, ensure_ascii=False, indent=2)
            print("分类规则已保存")
        except Exception as e:
            print(f"保存规则失败:{e}")

    def get_file_type(self, filename):
        """根据文件名获取文件类型"""
        _, ext = os.path.splitext(filename.lower())

        for category, extensions in self.rules.items():
            if ext in extensions:
                return category

        return "其他"

    def scan_files(self):
        """扫描文件夹中的所有文件"""
        files_info = []

        for item in os.listdir(self.folder_path):
            item_path = os.path.join(self.folder_path, item)

            # 跳过文件夹
            if os.path.isdir(item_path):
                continue

            # 跳过隐藏文件和规则文件
            if item.startswith(".") or item == "organizer_rules.json":
                continue

            file_type = self.get_file_type(item)
            files_info.append({
                "name": item,
                "type": file_type,
                "path": item_path
            })

        return files_info

    def preview_operations(self, files_info):
        """预览将要执行的操作"""
        if not files_info:
            print("没有需要整理的文件!")
            return

        print("\n=== 预览操作 ===")
        print(f"共找到 {len(files_info)} 个文件需要整理\n")

        # 按类型分组
        type_groups = {}
        for file_info in files_info:
            file_type = file_info["type"]
            if file_type not in type_groups:
                type_groups[file_type] = []
            type_groups[file_type].append(file_info)

        # 显示每个类型的文件
        for file_type, files in type_groups.items():
            target_folder = os.path.join(self.folder_path, file_type)
            print(f"\n{file_type}】 -> {target_folder}")
            for file_info in files:
                print(f"  - {file_info['name']}")

        return type_groups

    def organize(self, dry_run=False):
        """整理文件"""
        if not os.path.exists(self.folder_path):
            print(f"文件夹不存在:{self.folder_path}")
            return False

        # 扫描文件
        files_info = self.scan_files()
        if not files_info:
            print("没有需要整理的文件!")
            return False

        # 预览操作
        type_groups = self.preview_operations(files_info)

        if dry_run:
            print("\n【预览模式】不会实际执行操作")
            return True

        # 确认操作
        print("\n=== 开始整理 ===")
        confirm = input("确定要整理吗?(输入yes确认):")
        if confirm.lower() != "yes":
            print("操作已取消")
            return False

        # 执行整理
        success_count = 0
        error_count = 0

        for file_type, files in type_groups.items():
            # 创建目标文件夹
            target_folder = os.path.join(self.folder_path, file_type)
            os.makedirs(target_folder, exist_ok=True)

            # 移动文件
            for file_info in files:
                try:
                    old_path = file_info["path"]
                    new_path = os.path.join(target_folder, file_info["name"])

                    # 检查文件是否已存在
                    if os.path.exists(new_path):
                        print(f"跳过(文件已存在):{file_info['name']}")
                        continue

                    # 移动文件
                    shutil.move(old_path, new_path)

                    # 记录操作(用于回滚)
                    self.operations.append({
                        "old_path": old_path,
                        "new_path": new_path,
                        "timestamp": datetime.now().isoformat()
                    })

                    print(f"移动:{file_info['name']} -> {file_type}/")
                    success_count += 1

                except Exception as e:
                    print(f"移动失败 {file_info['name']}{e}")
                    error_count += 1

        # 保存操作记录
        self.save_operations()

        print(f"\n整理完成!成功:{success_count},失败:{error_count}")
        return True

    def save_operations(self):
        """保存操作记录"""
        if not self.operations:
            return

        record_file = os.path.join(self.folder_path, "organizer_record.json")
        try:
            with open(record_file, "w", encoding="utf-8") as f:
                json.dump(self.operations, f, ensure_ascii=False, indent=2)
            print(f"操作记录已保存到:{record_file}")
        except Exception as e:
            print(f"保存操作记录失败:{e}")

    def rollback(self):
        """回滚操作"""
        record_file = os.path.join(self.folder_path, "organizer_record.json")

        if not os.path.exists(record_file):
            print("没有找到操作记录,无法回滚")
            return

        try:
            with open(record_file, "r", encoding="utf-8") as f:
                operations = json.load(f)

            if not operations:
                print("没有可回滚的操作")
                return

            print(f"\n=== 回滚操作 ===")
            print(f"共找到 {len(operations)} 个操作需要回滚")

            confirm = input("确定要回滚吗?(输入yes确认):")
            if confirm.lower() != "yes":
                print("回滚已取消")
                return

            success_count = 0
            error_count = 0

            for op in reversed(operations):
                try:
                    old_path = op["old_path"]
                    new_path = op["new_path"]

                    if os.path.exists(new_path):
                        shutil.move(new_path, old_path)
                        print(f"回滚:{os.path.basename(new_path)}")
                        success_count += 1
                    else:
                        print(f"跳过(文件不存在):{os.path.basename(new_path)}")

                except Exception as e:
                    print(f"回滚失败:{e}")
                    error_count += 1

            print(f"\n回滚完成!成功:{success_count},失败:{error_count}")

            # 删除操作记录
            os.remove(record_file)
            print("操作记录已删除")

        except Exception as e:
            print(f"回滚失败:{e}")

    def add_rule(self, category, extensions):
        """添加分类规则"""
        extensions = [ext if ext.startswith(".") else f".{ext}" for ext in extensions]

        if category not in self.rules:
            self.rules[category] = []

        for ext in extensions:
            if ext not in self.rules[category]:
                self.rules[category].append(ext)

        self.save_rules()
        print(f"已添加规则:【{category}{extensions}")

    def remove_rule(self, category, extensions):
        """删除分类规则"""
        if category not in self.rules:
            print(f"分类【{category}】不存在")
            return

        for ext in extensions:
            ext = ext if ext.startswith(".") else f".{ext}"
            if ext in self.rules[category]:
                self.rules[category].remove(ext)

        # 如果分类下没有扩展名了,删除分类
        if not self.rules[category]:
            del self.rules[category]

        self.save_rules()
        print(f"已删除规则:【{category}{extensions}")

    def show_rules(self):
        """显示所有分类规则"""
        print("\n=== 当前分类规则 ===")
        for category, extensions in self.rules.items():
            print(f"\n{category}】")
            for ext in extensions:
                print(f"  - {ext}")


def main():
    """主函数"""
    print("=== 文件整理工具 ===\n")

    # 输入文件夹路径
    folder_path = input("请输入要整理的文件夹路径(直接回车使用当前目录):")
    if not folder_path.strip():
        folder_path = os.getcwd()
        print(f"使用当前目录:{folder_path}")

    # 创建整理工具
    organizer = FileOrganizer(folder_path)

    while True:
        print("\n=== 主菜单 ===")
        print("1. 预览操作")
        print("2. 开始整理")
        print("3. 回滚操作")
        print("4. 查看分类规则")
        print("5. 添加分类规则")
        print("6. 删除分类规则")
        print("7. 退出")

        choice = input("\n请选择操作(1-7):")

        if choice == "1":
            files_info = organizer.scan_files()
            organizer.preview_operations(files_info)

        elif choice == "2":
            dry_run = input("是否只预览不执行?(yes/no):").lower() == "yes"
            organizer.organize(dry_run)

        elif choice == "3":
            organizer.rollback()

        elif choice == "4":
            organizer.show_rules()

        elif choice == "5":
            category = input("请输入分类名称:")
            extensions = input("请输入扩展名(用空格分隔,如 jpg png):").split()
            organizer.add_rule(category, extensions)

        elif choice == "6":
            category = input("请输入分类名称:")
            extensions = input("请输入要删除的扩展名(用空格分隔):").split()
            organizer.remove_rule(category, extensions)

        elif choice == "7":
            print("退出程序!")
            break

        else:
            print("无效的选择,请重新输入")


if __name__ == "__main__":
    main()

使用示例

1. 整理下载文件夹

# 简单使用
organizer = FileOrganizer("/Users/yourname/Downloads")
organizer.organize()

2. 预览操作

organizer = FileOrganizer("/Users/yourname/Downloads")
organizer.organize(dry_run=True)  # 只预览,不执行

3. 自定义分类规则

organizer = FileOrganizer("/Users/yourname/Downloads")

# 添加新的分类
organizer.add_rule("设计文件", ["psd", "ai", "sketch"])

# 删除规则
organizer.remove_rule("图片", ["bmp", "svg"])

4. 回滚操作

organizer = FileOrganizer("/Users/yourname/Downloads")
organizer.rollback()

进阶功能:按日期整理照片

import os
import shutil
from datetime import datetime

def organize_photos_by_date(folder_path):
    """按日期整理照片"""
    image_extensions = [".jpg", ".jpeg", ".png", ".heic"]

    for filename in os.listdir(folder_path):
        filepath = os.path.join(folder_path, filename)

        # 跳过文件夹
        if not os.path.isfile(filepath):
            continue

        # 只处理图片文件
        _, ext = os.path.splitext(filename.lower())
        if ext not in image_extensions:
            continue

        # 获取文件修改时间
        file_time = os.path.getmtime(filepath)
        date = datetime.fromtimestamp(file_time)
        date_folder = date.strftime("%Y-%m-%d")

        # 创建日期文件夹
        target_folder = os.path.join(folder_path, date_folder)
        os.makedirs(target_folder, exist_ok=True)

        # 移动文件
        target_path = os.path.join(target_folder, filename)
        shutil.move(filepath, target_path)

        print(f"移动:{filename} -> {date_folder}/")

    print("整理完成!")

# 使用示例
organize_photos_by_date("/Users/yourname/Photos")

本章小结

  • 自动整理文件夹:识别文件类型、按分类移动文件
  • 预览操作:查看将要执行的操作,避免误操作
  • 回滚功能:支持撤销操作,不用担心整理错了
  • 自定义规则:可以添加自己的分类规则
  • 进阶功能:按日期整理照片

这个工具可以帮你每天节省30分钟整理文件的时间!下一章我们来批量处理Excel,再也不加班!

继续学下去,马上就能做实用项目了!