shutil.unpack_archive支持主流格式但存在路径遍历风险,需校验路径、指定隔离目录;.rar/.7z需调用外部命令并做异常处理;应通过魔数识别真实格式;批量解压须限并发、查磁盘、及时清理。

用 shutil.unpack_archive 覆盖主流格式但要注意路径安全
shutil.unpack_archive 是 Python 3.5+ 内置方案,支持 .zip、.tar、.tar.gz、.tgz、.tar.bz2、.tar.xz,无需额外安装库。但它默认不校验压缩包内文件路径,遇到 ../../etc/passwd 这类路径会直接写入系统关键位置。
实操建议:
- 解压前先用
shutil.register_unpack_format自定义 handler,对每个成员调用os.path.normpath并检查是否以".."开头或含绝对路径 - 始终指定
extract_dir参数,避免默认解到当前目录;推荐用tempfile.mkdtemp()创建隔离目录 - 对
.rar和.7z等格式,shutil原生不支持,需跳转到下一条处理
处理 .rar 和 .7z 必须依赖外部命令和异常兜底
Python 没有稳定跨平台的纯库支持 .rar(rarfile 仅限 Unix + 安装 unrar)和 .7z(py7zr 支持有限,加密/分卷常失败)。最可靠方式是调用系统命令。
实操建议:
- 用
shutil.which("7z")或shutil.which("unrar")检查命令是否存在,不存在则跳过或报明确错误 - 执行命令时加
timeout=30防止卡死,捕获subprocess.TimeoutExpired和subprocess.CalledProcessError - Windows 下
7z默认路径可能不在PATH,可预设常见安装路径如C:\Program Files\7-Zip\7z.exe并尝试调用 - 命令参数务必用列表形式传入(如
["7z", "x", archive_path, f"-o{output_dir}"]),避免 shell 注入
识别压缩包类型不能只看后缀,要读文件头
用户可能把 .zip 文件重命名为 .txt,或用非标准后缀(如 .myzip)。单靠 os.path.splitext 判定格式会漏掉这类情况。
实操建议:
- 用
open(path, "rb").read(4)读前 4 字节比对魔数:ZIP 是b"PK\x03\x04",GZIP 是b"\x1f\x8b",BZ2 是b"BZh",7z 是b"7z\xbc\xaf\x27\x1c" - 对 tar 类型,仅靠魔数不够(
.tar无固定头),需结合后缀 + 是否能被tarfile.is_tarfile识别 - 若魔数匹配多个格式(如某些加密 ZIP 头被修改),优先按后缀 fallback,再抛出警告而非静默失败
批量解压时必须控制并发和磁盘空间,否则容易崩
一次解压上百个大压缩包,不加限制会导致内存暴涨、IO 打满、临时目录占满磁盘。Python 的 threading 对 IO 密集任务收益有限,且 subprocess 调用外部解压命令本身已占较多资源。
实操建议:
- 用
concurrent.futures.ThreadPoolExecutor(max_workers=2)严格限制并发数,避免同时跑 10 个7z进程 - 解压前用
shutil.disk_usage(output_root).free检查剩余空间,预留至少压缩包总大小 × 2 的空闲空间 - 每个任务完成后立即
shutil.rmtree(temp_extract_dir),不要等全部结束再清理 - 记录每个文件的解压状态(成功/失败/跳过)到 JSON 文件,方便断点续跑
真正麻烦的是混合格式里夹着损坏包、密码保护包、或需要交互式确认的场景——这些没法全自动绕过,脚本里得留好日志入口和人工干预标记位。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











