Python重復(fù)文件批量整理工具的設(shè)計(jì)與實(shí)現(xiàn)
1. 簡(jiǎn)介
這款文件整理工具主要用于在文件夾內(nèi)對(duì)文件進(jìn)行去重和分類(lèi)存儲(chǔ),尤其適用于處理圖片等文件類(lèi)型。用戶(hù)可以通過(guò)選擇源目錄、目標(biāo)目錄、重復(fù)文件目錄以及非重復(fù)文件目錄,快速完成文件分類(lèi)和去重工作。工具支持讀取 MD5 列表來(lái)識(shí)別重復(fù)文件,并根據(jù)文件的修改時(shí)間進(jìn)行分類(lèi)存儲(chǔ)。此外,工具還支持多線程處理,避免界面卡頓,提升用戶(hù)體驗(yàn)。
功能
1.目錄選擇和配置:
- 支持設(shè)置基準(zhǔn)目錄、目標(biāo)目錄、重復(fù)文件目錄和非重復(fù)文件目錄。
- 支持通過(guò)瀏覽按鈕快速選擇文件夾路徑。
2.文件去重和分類(lèi):
- 使用 MD5 校驗(yàn)碼對(duì)文件進(jìn)行去重,判斷文件是否重復(fù)。
- 如果啟用了 MD5 列表,工具會(huì)加載先前保存的 MD5 列表,以加速去重過(guò)程。
- 根據(jù)文件的修改時(shí)間,自動(dòng)將文件按年和月分類(lèi)存儲(chǔ)。
3.多線程處理:
- 采用多線程技術(shù),使得文件處理過(guò)程不阻塞主線程,確保界面保持響應(yīng)。
- 文件的移動(dòng)操作在后臺(tái)線程中進(jìn)行,避免影響 UI 界面的流暢性。
4.支持圖片文件類(lèi)型:
- 支持常見(jiàn)圖片格式,如 JPEG、PNG、BMP 和 HEIC。
- 對(duì) HEIC 文件進(jìn)行特殊處理,提取文件的 EXIF 時(shí)間戳,作為文件分類(lèi)的依據(jù)。
5.日志記錄:
- 提供實(shí)時(shí)日志記錄功能,記錄文件處理的詳細(xì)過(guò)程。
- 支持日志批量更新,減少對(duì) UI 的頻繁更新,提高性能。
6.MD5 列表管理:
可加載和保存 MD5 列表,記錄基準(zhǔn)文件的 MD5 值,幫助后續(xù)識(shí)別重復(fù)文件。
使用方法
配置目錄:
- 啟動(dòng)工具后,首先配置四個(gè)文件夾路徑:
- 基準(zhǔn)目錄:包含待處理文件的源目錄。
- 目標(biāo)目錄:包含待比對(duì)的目標(biāo)文件夾。
- 重復(fù)文件目錄:存放重復(fù)文件的目錄。
- 非重復(fù)文件目錄:存放未重復(fù)文件的目錄。
選擇選項(xiàng):
- 啟用“讀取 MD5 列表”選項(xiàng)來(lái)加載已保存的 MD5 列表。
- 啟用“啟用分類(lèi)存儲(chǔ)”選項(xiàng)將文件按年和月進(jìn)行分類(lèi)存儲(chǔ)。
開(kāi)始處理:
- 點(diǎn)擊“開(kāi)始處理”按鈕,工具會(huì)自動(dòng)掃描源目錄和目標(biāo)目錄,識(shí)別重復(fù)文件并進(jìn)行分類(lèi)存儲(chǔ)。
- 處理過(guò)程中,工具會(huì)在日志框中顯示詳細(xì)的操作信息。
文件分類(lèi)與去重:
- 工具根據(jù)文件的 MD5 校驗(yàn)碼判斷是否為重復(fù)文件,重復(fù)文件將移動(dòng)到指定的“重復(fù)文件目錄”。
- 未重復(fù)的文件將按其修改時(shí)間(年月)分類(lèi),存放到指定的“非重復(fù)文件目錄”中。
完成處理:
文件處理完成后,工具會(huì)在日志框中顯示相關(guān)信息,并自動(dòng)保存更新的 MD5 列表到“非重復(fù)文件目錄”中。
2. 運(yùn)行效果

3.相關(guān)源碼
import os
import hashlib
import shutil
import json
import threading
import re
import io
import exifread
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from datetime import datetime
from PIL import Image
from PIL.ExifTags import TAGS
import pillow_heif
from pillow_heif import register_heif_opener
class FileOrganizerApp:
def __init__(self, root):
self.root = root
self.root.title("文件整理工具")
# 變量初始化
self.base_dir = tk.StringVar()
self.target_dir = tk.StringVar()
self.duplicate_dir = tk.StringVar()
self.unique_dir = tk.StringVar()
self.enable_classify = tk.BooleanVar(value=True)
self.enable_md5list = tk.BooleanVar(value=False)
self.dictfile_name = "base_md5_list.txt"
# 創(chuàng)建register_heif_opener模塊里的一個(gè)類(lèi)然后再I(mǎi)mage.open打開(kāi)HEIC文件
register_heif_opener()
# 創(chuàng)建界面組件
self.create_widgets()
def create_widgets(self):
# 目錄選擇部分
dir_frame = ttk.LabelFrame(self.root, text="目錄配置")
dir_frame.pack(padx=10, pady=5, fill=tk.X)
self.create_dir_selector(dir_frame, "基準(zhǔn)目錄:", self.base_dir, 0)
self.create_dir_selector(dir_frame, "目標(biāo)目錄:", self.target_dir, 1)
self.create_dir_selector(dir_frame, "重復(fù)文件目錄:", self.duplicate_dir, 2)
self.create_dir_selector(dir_frame, "非重復(fù)文件目錄:", self.unique_dir, 3)
# 選項(xiàng)配置
opt_frame = ttk.LabelFrame(self.root, text="選項(xiàng)配置")
opt_frame.pack(padx=10, pady=5, fill=tk.X)
ttk.Checkbutton(opt_frame, text="啟用分類(lèi)存儲(chǔ)", variable=self.enable_classify).pack(anchor=tk.W)
ttk.Checkbutton(opt_frame, text="讀取MD5列表", variable=self.enable_md5list).pack(anchor=tk.W)
# 操作按鈕
btn_frame = ttk.Frame(self.root)
btn_frame.pack(padx=10, pady=5, fill=tk.X)
ttk.Button(btn_frame, text="開(kāi)始處理", command=self.start_processing).pack(side=tk.LEFT)
# 日志顯示
self.log_text = tk.Text(self.root, height=15)
self.scrollbar = tk.Scrollbar(root, command=self.log_text.yview)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.log_text.config(yscrollcommand=self.scrollbar.set)
self.log_text.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)
def create_dir_selector(self, parent, label, var, row):
frame = ttk.Frame(parent)
frame.grid(row=row, column=0, sticky="ew", padx=5, pady=2)
ttk.Label(frame, text=label).pack(side=tk.LEFT)
entry = ttk.Entry(frame, textvariable=var, width=40)
entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
ttk.Button(frame, text="瀏覽...", command=lambda: self.select_directory(var)).pack(side=tk.LEFT)
def select_directory(self, var):
dir_path = filedialog.askdirectory()
if dir_path:
var.set(dir_path)
def log(self, message):
# 批量更新日志,減少頻繁刷新UI
self.log_text.insert(tk.END, message + "\n")
self.root.update_idletasks()
def calculate_md5(self, filepath):
hash_md5 = hashlib.md5()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
def save_dict_to_file(self, dictionary, file_path):
with open(file_path, 'w', encoding='utf-8') as file:
json.dump(dictionary, file, ensure_ascii=False, indent=4)
def load_dict_from_file(self, file_path):
with open(file_path, 'r', encoding='utf-8') as file:
return json.load(file)
def get_unique_filename(self, dest_dir, filename):
base, ext = os.path.splitext(filename)
counter = 1
new_name = filename
while os.path.exists(os.path.join(dest_dir, new_name)):
new_name = f"{base}_{counter}{ext}"
counter += 1
return new_name
def is_image(self, file_path):
ext = os.path.splitext(file_path)[1].lower()
return ext in [".jpg", ".jpeg", ".png", ".bmp", ".heic"]
def get_file_time(self, file_path):
mtime = os.path.getmtime(file_path)
ext = os.path.splitext(file_path)[1].lower()
if self.is_image(file_path):
if ext == ".heic":
mtime = self.get_heic_original(file_path)
mtime = self.replace_limited(mtime, ':', '-', 2)
dt_object = datetime.strptime(mtime, "%Y-%m-%d %H:%M:%S")
mtime = dt_object.timestamp()
else:
with open(file_path, 'rb') as f:
tags = exifread.process_file(f, details=False)
if 'EXIF DateTimeOriginal' in tags:
mtime = str(tags['EXIF DateTimeOriginal'])
mtime = self.replace_limited(mtime, ':', '-', 2)
dt_object = datetime.strptime(mtime, "%Y-%m-%d %H:%M:%S")
mtime = dt_object.timestamp()
return mtime
def process_files(self):
base_files = {}
if self.enable_md5list.get():
base_md5_path = os.path.join(self.base_dir.get(), self.dictfile_name)
if os.path.exists(base_md5_path):
base_files = self.load_dict_from_file(base_md5_path)
self.log(f"基準(zhǔn)MD5已加載: {base_md5_path}")
else:
for root, _, files in os.walk(self.base_dir.get()):
for file in files:
path = os.path.join(root, file)
md5 = self.calculate_md5(path)
base_files[md5] = file
self.log(f"基準(zhǔn)文件已掃描: {path}")
for root, _, files in os.walk(self.target_dir.get()):
for file in files:
src_path = os.path.join(root, file)
md5 = self.calculate_md5(src_path)
self.log(f"正在處理: {src_path}")
if md5 in base_files:
dest_dir = self.duplicate_dir.get()
dest_path = os.path.join(dest_dir, self.get_unique_filename(dest_dir, file))
shutil.move(src_path, dest_path)
self.log(f"重復(fù)文件已移動(dòng): {dest_path}")
else:
if self.enable_classify.get():
mtime = self.get_file_time(src_path)
date_dir_Y = datetime.fromtimestamp(mtime).strftime("%Y")
date_dir_m = datetime.fromtimestamp(mtime).strftime("%m")
dest_dir = os.path.join(self.unique_dir.get(), date_dir_Y, date_dir_m)
else:
dest_dir = self.unique_dir.get()
os.makedirs(dest_dir, exist_ok=True)
dest_path = os.path.join(dest_dir, self.get_unique_filename(dest_dir, file))
shutil.move(src_path, dest_path)
self.log(f"非重復(fù)文件已移動(dòng): {dest_path}")
dest_md5_path = os.path.join(self.unique_dir.get(), self.dictfile_name)
self.save_dict_to_file(base_files, dest_md5_path)
self.log(f"基準(zhǔn)MD5已輸出: {dest_md5_path}")
def start_processing(self):
try:
dirs = [
self.base_dir.get(),
self.target_dir.get(),
self.duplicate_dir.get(),
self.unique_dir.get()
]
for d in dirs:
if not d:
raise ValueError("所有目錄都必須設(shè)置")
os.makedirs(d, exist_ok=True)
# 啟動(dòng)新線程處理文件
threading.Thread(target=self.process_files, daemon=True).start()
messagebox.showinfo("完成", "文件處理已開(kāi)始,請(qǐng)稍候!")
except Exception as e:
messagebox.showerror("錯(cuò)誤", str(e))
self.log(f"錯(cuò)誤發(fā)生: {str(e)}")
if __name__ == "__main__":
root = tk.Tk()
app = FileOrganizerApp(root)
root.mainloop()
4.總結(jié)
這款文件整理工具是一個(gè)高效且易于使用的文件去重和分類(lèi)存儲(chǔ)工具。它結(jié)合了 MD5 校驗(yàn)和文件時(shí)間戳分類(lèi),確保用戶(hù)能夠快速識(shí)別重復(fù)文件并將文件按時(shí)間進(jìn)行合理分類(lèi)。工具采用了多線程技術(shù),有效避免了長(zhǎng)時(shí)間操作導(dǎo)致的界面卡頓,提供了流暢的用戶(hù)體驗(yàn)。通過(guò)日志記錄和 MD5 列表管理,用戶(hù)可以清晰地了解文件處理的每一步操作。此外,該工具支持多種常見(jiàn)的圖片格式,且對(duì) HEIC 文件進(jìn)行了專(zhuān)門(mén)的處理,使其適應(yīng)了現(xiàn)代文件管理的需求。
以上就是Python重復(fù)文件批量整理工具的設(shè)計(jì)與實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于Python整理重復(fù)文件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
安裝python依賴(lài)包psycopg2來(lái)調(diào)用postgresql的操作
這篇文章主要介紹了安裝python依賴(lài)包psycopg2來(lái)調(diào)用postgresql的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
主流Python語(yǔ)音轉(zhuǎn)文字(STT)庫(kù)實(shí)戰(zhàn)指南
語(yǔ)音轉(zhuǎn)文字的核心是通過(guò)信號(hào)處理、特征提取和模式識(shí)別將聲波轉(zhuǎn)換為文本,下面這篇文章主要介紹了主流Python語(yǔ)音轉(zhuǎn)文字(STT)庫(kù)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2026-01-01
Python中用pycurl監(jiān)控http響應(yīng)時(shí)間腳本分享
這篇文章主要介紹了Python中用pycurl監(jiān)控http響應(yīng)時(shí)間腳本分享,本文腳本實(shí)現(xiàn)監(jiān)控http相應(yīng)碼,響應(yīng)大小,建立連接時(shí)間,準(zhǔn)備傳輸時(shí)間,傳輸?shù)谝粋€(gè)字節(jié)時(shí)間,完成時(shí)間,需要的朋友可以參考下2015-02-02
Python實(shí)現(xiàn)Window路徑格式轉(zhuǎn)換為L(zhǎng)inux路徑格式的代碼
這篇文章主要介紹了Python實(shí)現(xiàn)Window路徑格式轉(zhuǎn)換為L(zhǎng)inux路徑格式的方法,文中通過(guò)代碼示例講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-07-07
Python統(tǒng)計(jì)文件中去重后uuid個(gè)數(shù)的方法
這篇文章主要介紹了Python統(tǒng)計(jì)文件中去重后uuid個(gè)數(shù)的方法,實(shí)例分析了Python正則匹配及字符串操作的相關(guān)技巧,需要的朋友可以參考下2015-07-07
python?包實(shí)現(xiàn)?time?時(shí)間管理操作
這篇文章主要介紹了python包實(shí)現(xiàn)time時(shí)間管理操作,文章通過(guò)獲取當(dāng)前時(shí)間戳,即當(dāng)前系統(tǒng)內(nèi)表示時(shí)間的一個(gè)浮點(diǎn)數(shù),下文更多相關(guān)內(nèi)容需要的小伙伴可以參考一下2022-04-04
關(guān)于PyQt5主窗口圖標(biāo)顯示問(wèn)題匯總
這篇文章主要介紹了關(guān)于PyQt5主窗口圖標(biāo)顯示問(wèn)題匯總,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
編寫(xiě)自定義的Django模板加載器的簡(jiǎn)單示例
這篇文章主要介紹了編寫(xiě)自定義的Django模板加載器的簡(jiǎn)單示例,Django是各色人氣Python框架中最為著名的一個(gè),需要的朋友可以參考下2015-07-07

