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 下无控制台窗口运行)
⚠️ 注意事项
- 不会修改原文件夹:所有操作都是复制,不会影响原始文件
- 磁盘空间:确保目标磁盘有足够空间存储所有提取的文件
- 权限问题:如果文件有读写权限限制,可能导致复制失败
- 大文件夹处理:文件数量较多时,处理可能需要一定时间,请耐心等待
- 重复文件:程序会为所有重名文件自动添加后缀,不会覆盖任何文件
🐛 常见问题
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()

2635

被折叠的 条评论
为什么被折叠?



