zipfile递归打包需设arcname为相对路径,解压前须校验路径防穿越,大文件应流式写入,中文名需统一UTF-8编码并设flag_bits,否则结构丢失、越界写入、OOM或乱码。

用 zipfile 递归打包文件夹时,路径处理不对会导致压缩包里全是平级文件
根本问题不在递归逻辑,而在 ZipFile.write() 的 arcname 参数没设对。默认直接写入文件名,所有文件都挤在根目录下,原始目录结构彻底丢失。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 必须手动构造相对路径作为
arcname:用os.path.relpath(file_path, start=src_dir)获取相对于源文件夹的路径 - 别用
shutil.make_archive图省事——它底层调用zipfile,但不暴露arcname控制权,结构照样扁平化 - 遍历推荐用
os.walk(),比pathlib.rglob()更易控制路径拼接逻辑 - 注意空文件夹不会被自动包含,需显式创建
ZipInfo并调用zipfile.writestr()(极少见需求,通常可忽略)
zipfile 解压到指定目录时,路径穿越漏洞会让文件写到系统任意位置
这是真实高危问题:zipfile 默认不做路径校验,如果压缩包里含 ../etc/passwd 这类恶意路径,解压会直接覆盖系统关键文件。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 解压前必须校验每个
ZipInfo.filename:用os.path.normpath()归一化后检查是否以".."开头或含os.sep + ".." + os.sep - 更稳妥的做法是用
os.path.commonpath([target_dir, extracted_path]) != target_dir判断是否越界 - 别依赖
zipfile.extractall(path=...)的path参数——它只控制根目录,不拦截非法子路径 - Python 3.12+ 可用
zipfile.Path类做安全遍历,但老版本仍需手动校验
打包大文件夹时内存暴涨或卡死,其实是 zipfile 缓存策略没调好
zipfile 默认把整个压缩流缓存在内存,遇到几百 MB 的日志目录或 node_modules,很容易触发 OOM 或假死。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 强制使用流式写入:初始化
ZipFile时加参数compression=zipfile.ZIP_DEFLATED(默认就是它),并确保不调用write()前读取整个文件内容 - 对单个大文件,改用
zipfile.writestr()+ 分块读取(open(..., 'rb').read(8192))避免一次性加载 - 别用
zipfile.write()直接传路径——它内部会全量读入;先open再writestr才可控 - Windows 下注意
zipfile对长路径支持差,超 260 字符可能报OSError: [WinError 206],需提前启用长路径支持或截断路径
跨平台解压后中文文件名乱码,本质是 ZIP 标准编码不统一
Windows 默认用 GBK 写 ZIP,Linux/macOS 用 UTF-8,zipfile 读取时不指定编码就会按系统 locale 解码,导致中文变问号或乱码。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- Python 3.7+ 可用
zipfile.ZipFile(..., strict_timestamps=False)配合zipfile.Path间接规避,但最稳方案是预处理:用zipfile.ZipFile.read()读原始字节,再手动.decode('gbk')或.decode('utf-8', errors='replace') - 打包方应主动声明 UTF-8:设置
zipinfo.flag_bits |= 0x800(ZIP 中文扩展标志位),但需确认解压端支持(7-Zip 支持,原生 Windows 解压器不支持) - 生产环境建议统一用
tar.gz替代 ZIP 处理含中文路径的备份——tarfile默认 UTF-8,无兼容性包袱










