日常使用 Windows 时,经常需要对文件进行压缩、加密或分卷存储。WinRAR 本身已经提供了比较完善的功能,但如果需要批量处理多个文件和文件夹,操作起来仍然比较繁琐。
因此,我使用 Python 开发了一个简单的批量压缩工具。其并不重新实现压缩算法,而是调用 WinRAR 完成压缩操作。

下载请见 Malvern’s File Server 。
功能介绍
工具基于 Python 标准库中的 tkinter.ttk 构建,主要支持以下功能:
| 功能 | 说明 |
|---|---|
| 批量压缩 | 支持一次选择多个文件或文件夹 |
| 压缩等级 | 提供存储、最快、较快、标准、较好、最好六档 |
| 文件加密 | 支持设置 RAR 压缩密码 |
| 文件名加密 | 可选择同时加密压缩包内部的文件名 |
| 恢复记录 | 支持添加 1%、2%、3%、5%、10% 的恢复记录 |
| 分卷压缩 | 支持 100 MB、500 MB、1 GB、2 GB、4 GB、4.7 GB、25 GB |
| 覆盖控制 | 可选择覆盖或跳过已经存在的压缩文件 |
| 运行日志 | 在窗口右侧显示 WinRAR 输出 |
| Windows 通知 | 压缩完成后可发送系统通知 |
| 深浅色模式 | 自动检测 Windows 应用主题并调整界面颜色 |
| 高 DPI 适配 | 支持高分辨率显示器 |
| 环境检测 | 启动时自动查找 WinRAR |
整个工具没有复杂的安装流程,只需要安装 Python 和 WinRAR 即可运行。Windows 通知功能属于可选功能,不会影响主要的压缩操作。
为什么选择 WinRAR?
Python 本身提供了 zipfile、tarfile 等标准库,可以直接创建 ZIP 或 TAR 压缩文件。
但这次开发有两个比较重要的需求:
- 支持压缩包加密,尤其是文件名加密。
- 支持恢复记录,提高压缩包在发生局部损坏时的可修复性。
WinRAR 的 RAR 格式能够同时满足这些需求,并且提供命令行接口,便于使用 Python 自动化调用。
压缩格式与数据恢复
压缩与纠错实际上是两个不同的概念。
普通压缩主要通过消除数据冗余减小文件体积,而恢复记录则需要额外保存用于修复损坏数据的信息。
如果压缩包中的一部分数据发生损坏,普通压缩文件可能无法正常解压。尤其是在使用固实压缩时,局部损坏可能影响后续多个文件。
WinRAR 提供了恢复记录功能,可以在创建 RAR 压缩包时额外写入一定比例的恢复数据。
例如:
1 | WinRAR.exe a -rr3p archive.rar "example" |
其中:
a:向压缩包添加文件。-rr3p:添加约 3% 的恢复记录。archive.rar:输出压缩包。example:待压缩的文件或文件夹。
需要注意,恢复记录并不意味着压缩包可以承受任意程度的损坏。
实际修复能力取决于损坏的位置、范围以及压缩包结构。恢复记录比例越高,通常需要付出的额外存储空间也越多。
对于需要长期保存的文件,可以适当提高恢复记录比例,但仍然不能用它替代独立备份。
开发环境
本项目使用以下技术:
| 项目 | 技术 |
|---|---|
| 操作系统 | Windows 10 / Windows 11 |
| 编程语言 | Python 3 |
| 图形界面 | Tkinter.ttk |
| 压缩引擎 | WinRAR |
| Windows API | ctypes |
| 系统主题检测 | winreg |
| 后台任务 | threading |
| 外部程序调用 | subprocess |
| Windows 通知 | winotify(可选) |
其中,除了可选的 winotify,其余 Python 模块均属于标准库。
如果需要启用 Windows 通知,可以通过 PowerShell 安装:
1 | pip install winotify |
如果不安装,程序仍然可以正常运行,只是不发送系统通知。
程序实现原理
整个程序可以分成三个部分:
- 使用 tkinter.ttk 创建图形界面。
- 根据用户选择生成 WinRAR 命令行参数。
- 通过 subprocess 调用 WinRAR,并将输出显示在日志窗口。
自动检测 WinRAR
程序启动时会尝试在常见安装目录中寻找 WinRAR.exe,比如:
1 | C:\Program Files\WinRAR\WinRAR.exe |
同时也会尝试从系统的 PATH 环境变量中寻找 WinRAR。
1 | def find_winrar(): |
如果未检测到 WinRAR,程序会询问是否打开官方下载页面,然后退出。
压缩等级
WinRAR 可以通过 -m 参数控制压缩等级,例如:
1 | WinRAR.exe a -m5 archive.rar "example" |
在 Python 中,通过字典将界面中的中文选项映射为 WinRAR 参数:
1 | mapping = { |
其中,-m0 表示不压缩,仅存储文件;-m5 是 RAR5 常用的标准压缩等级。
压缩等级越高,并不意味着所有文件都能明显缩小。
例如,JPEG 图片、MP4 视频以及已经压缩过的文件,通常很难通过再次压缩获得明显的体积收益。
文件与文件名加密
WinRAR 提供了两种相关的密码参数:
1 | -pPASSWORD |
以及:
1 | -hpPASSWORD |
两者的区别是:
| 参数 | 文件内容加密 | 文件名加密 |
|---|---|---|
-p | 是 | 否 |
-hp | 是 | 是 |
因此,程序通过复选框控制是否加密文件名:
1 | password = self.password_var.get() |
使用文件名加密后,未输入正确密码的用户通常无法查看压缩包内部的文件列表。
需要注意,当前程序通过命令行参数向 WinRAR 传递密码。在压缩进程运行期间,具有足够权限的本机进程可能读取命令行参数,因此不适合在不可信的共享计算机上处理高敏感密码。
添加恢复记录
恢复记录通过 -rr 参数实现。
程序提供以下选项:
1 | RECOVERY_OPTIONS = [ |
生成命令行参数:
1 | if self.recovery_var.get(): |
例如选择 5%,生成:
1 | -rr5p |
恢复记录可以提高压缩包发生局部损坏后的可修复性,但它并不是独立备份,也不能保证所有损坏都能恢复。
分卷压缩
分卷压缩适合将大型文件拆分成多个较小的压缩文件。
例如,需要将一个大型文件保存到单个文件大小受限的存储设备,或者分批传输到其他计算机时,可以使用分卷压缩。
WinRAR 通过 -v 参数设置分卷大小。
例如:
1 | WinRAR.exe a -v1g archive.rar "example" |
表示将压缩结果按约 1 GB 的大小分卷。
程序使用字典维护分卷选项:
1 | VOLUME_MAP = { |
生成参数:
1 | volume_size = VOLUME_MAP.get(self.volume_var.get()) |
分卷后的压缩文件通常采用以下命名方式:
1 | archive.part1.rar |
最后一个分卷的大小通常小于设定值。
解压时,应将所有分卷放在同一目录中,并从第一个分卷开始解压。
需要注意,分卷本身并不提供数据冗余。如果缺失其中一个分卷,即使其他分卷没有损坏,也可能无法完整恢复原始文件。
防止界面卡死
如果直接在 Tkinter 主线程中执行压缩命令,压缩期间窗口可能无法响应鼠标操作,甚至被 Windows 标记为 “未响应”。
因此,程序使用 threading 将压缩任务放到后台线程执行:
1 | worker = threading.Thread( |
实际压缩操作通过 subprocess.Popen() 调用 WinRAR:
1 | process = subprocess.Popen( |
随后读取 WinRAR 输出,并显示到右侧日志区域。
由于 Tkinter 不适合直接从后台线程修改界面控件,程序通过 root.after() 将界面更新操作安排到主线程执行:
1 | self.root.after(0, append) |
这样既能保证窗口正常响应,也可以实时查看压缩进度和错误信息。
Windows 11 深浅色模式适配
Tkinter 是一个跨平台 GUI 库,但传统 ttk 控件并不能完整、自动地适配 Windows 11 的深色模式。
因此,程序采用了系统主题检测与手动设置控件颜色相结合的方案。
检测系统主题
Windows 会在注册表中保存应用主题设置:
1 | HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize |
其中:
1 | AppsUseLightTheme = 0 |
表示深色模式。
1 | AppsUseLightTheme = 1 |
表示浅色模式。
Python 可以使用 winreg 读取这一设置:
1 | def is_dark_mode(): |
程序每隔 500 毫秒检查一次主题设置。
如果用户在 Windows 设置中切换深色或浅色模式,程序也会相应调整界面。
标题栏适配
普通 Tkinter 窗口的标题栏由 Windows 管理。
为了使标题栏也能够跟随系统主题,可以通过 ctypes 调用 Windows DWM API:
1 | def set_dark_titlebar(hwnd, dark): |
其中,属性编号 20 对应 DWMWA_USE_IMMERSIVE_DARK_MODE。
需要注意,具体效果取决于 Windows 版本。
输入框深色模式白底问题
在实际开发中,我遇到一个问题:
即使已经给 ttk.Entry 设置了深色样式,密码输入框内部仍然可能显示白色背景。
最终的解决方法是将密码输入框改为原生 tk.Entry,并直接控制背景色、文字颜色、光标颜色和选中区域颜色。
1 | self.password_entry = tk.Entry( |
深色模式下:
1 | self.password_entry.configure( |
浅色模式下:
1 | self.password_entry.configure( |
这种方式比单纯修改 ttk 样式更容易控制输入框的实际显示效果。
高 DPI 适配
在高分辨率显示器上,传统 Tkinter 程序可能出现界面模糊或缩放异常。
程序通过 Windows API 设置进程 DPI 感知:
1 | ctypes.windll.user32.SetProcessDpiAwarenessContext(ctypes.c_void_p(-4)) |
其中 -4 对应 Per-Monitor DPI Aware V2。
需要注意,这项设置必须在创建 Tk 窗口之前执行。
另外,程序没有指定自定义字体,而是尽量使用 Tkinter 默认字体,以减少高 DPI 环境下的字体显示问题。
总结
这次开发并没有涉及复杂的压缩算法,而是通过 Python 将 WinRAR 已有的命令行功能整合到一个简单的图形界面中。
开发过程中,比较值得记录的技术点包括:
- 使用
subprocess调用外部程序。 - 使用
threading避免 Tkinter 界面卡死。 - 使用
root.after()实现线程安全的界面更新。 - 使用
winreg检测 Windows 应用主题。 - 使用
ctypes调用 Windows API。 - 使用
tk.Entry解决 ttk 输入框深色模式适配问题。 - 使用 WinRAR 的命令行参数实现加密、恢复记录和分卷压缩。