python(52) : 文件夹内文件递归提取工具[Windows工具(6)]

1.说明

📝 简介

一款简单易用的文件提取工具,支持通过拖拽操作将文件夹内所有文件(包括子目录中的文件)扁平化提取到一个新文件夹中。

✨ 功能特性

  • 🖱️ 拖拽操作:简单直观的图形界面,支持拖拽文件夹
  • 📁 递归提取:自动遍历所有子目录,提取全部文件
  • 📋 扁平化输出:将所有文件复制到同一层级,不保留原目录结构
  • 🔄 智能重命名
    • 自动处理重名文件(添加 (重复1)(重复2) 等后缀)
    • 自动处理重名文件夹(添加 (1)(2) 等后缀)
  • 💾 保留元数据:使用 shutil.copy2 保留文件原始时间戳和权限信息
  • ✅ 实时反馈:显示复制文件数量和目标路径

🚀 使用方法

1. 安装依赖

pip install tkinterdnd2

2. 运行程序

双击运行 文件夹文件提取.pyw 或在命令行中执行:

python 文件夹文件提取.pyw

3. 拖拽文件夹

将需要提取的文件夹拖拽到程序窗口中的指定区域即可。

📂 输出说明

输出位置

新文件夹会创建在原文件夹的同级目录下。

命名规则

  • 默认命名原文件夹名-全
  • 重名处理:如果已存在同名文件夹,会自动添加序号
    • 例如:data-全 → data-全(1) → data-全(2)

文件重名处理

如果提取过程中遇到同名文件,会自动重命名:

原文件:file.txt
如果已存在,重命名为:file(重复1).txt
如果仍存在,重命名为:file(重复2).txt
...以此类推

💡 使用示例

示例 1:基本使用

操作前:

📁 项目文档/
  ├── 📁 设计稿/
  │   ├── 图1.png
  │   └── 图2.png
  ├── 📁 需求/
  │   └── 需求文档.docx
  └── 说明.txt

拖拽 "项目文档" 文件夹到工具窗口

操作后:

📁 项目文档/(原文件夹,保持不变)
📁 项目文档-全/(新创建的文件夹)
  ├── 图1.png
  ├── 图2.png
  ├── 需求文档.docx
  └── 说明.txt

示例 2:处理重名文件

操作前:

📁 资料/
  ├── 📁 A/
  │   └── 文件.txt(内容:AAA)
  └── 📁 B/
      └── 文件.txt(内容:BBB)

操作后:

📁 资料-全/
  ├── 文件(重复0).txt(内容:AAA)
  └── 文件(重复1).txt(内容:BBB)

⚙️ 技术实现

  • GUI 框架:tkinter
  • 拖拽支持:tkinterdnd2
  • 文件操作:os、shutil
  • 文件类型:.pyw(Windows 下无控制台窗口运行)

⚠️ 注意事项

  1. 不会修改原文件夹:所有操作都是复制,不会影响原始文件
  2. 磁盘空间:确保目标磁盘有足够空间存储所有提取的文件
  3. 权限问题:如果文件有读写权限限制,可能导致复制失败
  4. 大文件夹处理:文件数量较多时,处理可能需要一定时间,请耐心等待
  5. 重复文件:程序会为所有重名文件自动添加后缀,不会覆盖任何文件

🐛 常见问题

Q: 为什么拖拽后没有反应?

A: 请确保拖拽的是文件夹而不是文件,工具仅支持文件夹拖拽。

Q: 可以处理隐藏文件吗?

A: 可以,程序会提取所有文件,包括隐藏文件。

Q: 提取后的文件顺序是什么?

A: 文件提取顺序取决于操作系统的文件遍历顺序,通常按字母顺序。

📄 许可证

本工具为个人学习项目,可自由使用和修改。

🔗 相关工具

同目录下还有其他实用工具:

  • 同名文件比较.pyw - 比较两个文件夹中的同名文件
  • 添加后缀.pyw - 批量为文件添加后缀
  • 将文件夹内文件等分.pyw - 将文件夹内文件平均分配到多个子文件夹

2.python代码

import os
import shutil
import tkinter as tk
from tkinterdnd2 import DND_FILES, TkinterDnD

chongfu =[]

def get_unique_folder_name(base_path):
    """返回唯一不重复的文件夹路径,如:data全 -> data全(1)"""
    if not os.path.exists(base_path):
        return base_path
    counter = 1
    while True:
        new_path = f"{base_path}({counter})"
        if not os.path.exists(new_path):
            return new_path
        counter += 1


def get_unique_filename_in_folder(dest_folder, filename):
    """在目标文件夹中生成不重复的文件名:file.txt → file(重复1).txt"""
    name, ext = os.path.splitext(filename)
    new_name = filename
    counter = 1
    while os.path.exists(os.path.join(dest_folder, new_name)):
        f = os.path.join(dest_folder, new_name)
        if f not in chongfu:
            chongfu.append(f)
        new_name = f"{name}(重复{counter}){ext}"
        counter += 1
    return new_name


def copy_all_files_flat(src_folder, dest_folder):
    """
    递归遍历 src_folder 中所有文件(包括子目录)
    将所有文件扁平化复制到 dest_folder(不保留目录结构)
    自动处理重名
    """
    os.makedirs(dest_folder, exist_ok=True)
    copied_count = 0

    for root, dirs, files in os.walk(src_folder):
        for file in files:
            src_file_path = os.path.join(root, file)
            dest_name = get_unique_filename_in_folder(dest_folder, file)
            dest_file_path = os.path.join(dest_folder, dest_name)

            try:
                shutil.copy2(src_file_path, dest_file_path)  # 保留元数据
                copied_count += 1
            except Exception as e:
                print(f"复制失败: {src_file_path} -> {dest_file_path}, 错误: {e}")

    return copied_count


def on_drop(event):
    # 获取拖入的路径(去除花括号)
    folder_path = event.data.strip('{}')

    if not os.path.isdir(folder_path):
        status_label.config(text="❌ 错误:请拖入一个有效的文件夹!", fg="red")
        return

    # 获取原文件夹名称和父目录
    folder_name = os.path.basename(folder_path)
    parent_dir = os.path.dirname(folder_path)

    # 目标文件夹名:原名 + "全"
    new_folder_name = folder_name + "-全"
    dest_folder_base = os.path.join(parent_dir, new_folder_name)
    dest_folder = get_unique_folder_name(dest_folder_base)

    try:
        count = copy_all_files_flat(folder_path, dest_folder)
        status_label.config(
            text=f"✅ 成功:已复制 {count} 个文件到\n{dest_folder}",
            fg="green"
        )
        for i in chongfu:
            basename = os.path.basename(i)
            dirname = os.path.dirname(i)
            ss = basename.split('.')
            x = dirname+os.sep+ss[0]+'(重复0).'+ss[1]
            os.rename(i,x)
    except Exception as e:
        status_label.config(text=f"❌ 错误:{str(e)}", fg="red")


# ========== GUI 界面 ==========
root = TkinterDnD.Tk()
root.title("文件提取工具")
root.geometry("500x300")

# 拖拽区域
drop_label = tk.Label(
    root,
    text="文件提取工具\n\n(请将文件夹拖拽到此处)",
    bg="lightblue",
    fg="black",
    font=("微软雅黑", 12),
    relief="solid",
    width=50,
    height=6,
    justify="center"
)
drop_label.pack(padx=20, pady=10)

# 状态显示区域(不随拖拽重置)
status_label = tk.Label(
    root,
    text="等待拖入文件夹...",
    bg="white",
    fg="gray",
    font=("微软雅黑", 10),
    anchor="w",
    justify="left",
    height=4,
    wraplength=480,
    relief="sunken"
)
status_label.pack(padx=20, pady=10, fill="x")

# 启用拖拽功能
drop_label.drop_target_register(DND_FILES)
drop_label.dnd_bind('<<Drop>>', on_drop)

# 运行主循环
root.mainloop()

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值