当前位置:首页 > 文章列表 > 文章 > python教程 > Python zipfile 解压为什么会报 BadZipFile:先验签文件头再处理成员路径

Python zipfile 解压为什么会报 BadZipFile:先验签文件头再处理成员路径

来源:17golang原创 2026-08-28 12:15:59 0浏览 收藏

上传接口收到一个名为 backup.zip 的文件,Python 却在 zipfile.ZipFile 这一行抛出 BadZipFile: File is not a zip file。先别急着把扩展名改成 .zip:这个异常通常说明文件头不是 ZIP、文件还没写完整,或者上游把 HTML 错误页保存成了压缩包名。可靠的处理顺序是先用 is_zipfile 做快速判断,再在 with ZipFile(...) 中打开并检查成员,最后才允许解压到受控目录。

要点速览
  • is_zipfile 只能做文件头级别的快速筛选,不能替代完整读取和成员校验。
  • BadZipFile 要从内容来源、写入是否完成、文件是否被截断三条线排查。
  • 解压前要拒绝绝对路径和 ../ 穿越路径,目标路径必须落在受控目录内。
  • 用临时目录完成解压和清单核对,成功后再原子切换到业务目录。

先确认异常发生在文件头还是完整读取

zipfile.is_zipfile 适合放在上传入口,快速识别文件是否像一个有效 ZIP。它可以接收文件路径,也可以接收可定位的文件对象;返回 False 时不要继续尝试解压。返回 True 也只代表文件头检查通过,后续打开成员仍可能发现损坏。

from pathlib import Path
from zipfile import BadZipFile, ZipFile, is_zipfile

def inspect_zip(path: Path) -> list[str]:
    if not is_zipfile(path):
        raise ValueError("upload is not a ZIP archive")

    try:
        with ZipFile(path) as archive:
            return archive.namelist()
    except BadZipFile as exc:
        raise ValueError("ZIP header passed, but archive is damaged") from exc

这里有两个有意分开的状态:文件头不匹配时返回“不是 ZIP”,打开过程中抛出 BadZipFile 时返回“结构损坏”。日志里保留文件大小、上传请求编号和上游响应类型,通常比只记录扩展名更容易找到真正原因。

Python is_zipfile 快速验证后进入 ZipFile 上下文并区分文件头失败与 BadZipFile 结构损坏

BadZipFile 的三条排查线不要混在一起

上游返回的可能不是压缩包

下载接口返回 403、登录页或 JSON 错误时,保存代码如果只看 URL 后缀,最后得到的就是一个名叫 backup.zip 的普通文本文件。保存前先核对 HTTP 状态、响应类型和实际字节长度;不要把服务端错误页交给 ZipFile 猜。

写入尚未完成就开始读取

如果一个线程还在写临时文件,另一个线程已经拿它创建 ZipFile,中央目录可能尚未落盘。让生产者先关闭文件,再把完整路径交给消费者;用临时文件名和完成标记区分“正在下载”和“可以读取”。

文件中途被截断

网络断开、磁盘空间不足或上传分片缺失,都可能让文件头还在但中央目录不完整。这类问题通常会在打开或读取成员时暴露,应该保留原始大小与校验摘要,方便和发送端记录比对。

解压前把成员路径收敛到安全目录

压缩包能正常打开,不代表可以无条件调用 extractall。成员名可能包含绝对路径或父目录片段,直接拼接目标目录会把文件写到预期目录之外。下面的函数先规范化成员路径,再确认它仍位于目标目录内;目录项只创建目录,不写入文件。

from pathlib import Path
from zipfile import ZipFile

def extract_to_safe(archive_path: Path, target_dir: Path) -> list[Path]:
    target_dir.mkdir(parents=True, exist_ok=True)
    extracted: list[Path] = []

    with ZipFile(archive_path) as archive:
        for member in archive.infolist():
            member_path = Path(member.filename)
            if member_path.is_absolute() or ".." in member_path.parts:
                raise ValueError(f"unsafe ZIP member: {member.filename}")

            destination = (target_dir / member_path).resolve()
            if destination != target_dir.resolve() and target_dir.resolve() not in destination.parents:
                raise ValueError(f"outside target directory: {member.filename}")
            if member.is_dir():
                destination.mkdir(parents=True, exist_ok=True)
                continue
            destination.parent.mkdir(parents=True, exist_ok=True)
            with archive.open(member) as source, destination.open("wb") as output:
                output.write(source.read())
            extracted.append(destination)
    return extracted

路径校验和文件写入是同一条数据路径上的两个步骤:先从 member.filename 得到候选位置,再由 destination 判断是否仍在 target_dir,最后才打开源成员和目标文件。不要先写文件再补做检查。

Python ZipFile 成员 filename 经过绝对路径与父目录检查后写入 target_dir 的安全数据路径

用临时目录完成核对,再提交解压结果

业务代码不应边解压边让其他请求读取目标目录。可以为每次任务创建一个临时目录,完成成员数量、关键文件和大小检查后,再把临时目录切换成正式目录。若任何成员读取失败,清理临时目录,保留错误与归档摘要。

from tempfile import TemporaryDirectory

def unpack_job(archive_path: Path, final_dir: Path) -> int:
    with TemporaryDirectory(prefix="zip-check-") as tmp:
        staged = Path(tmp)
        files = extract_to_safe(archive_path, staged)
        if not files or not any(path.name == "manifest.json" for path in files):
            raise ValueError("archive has no usable manifest.json")
        final_dir.parent.mkdir(parents=True, exist_ok=True)
        staged.rename(final_dir)
        return len(files)

示例把“解压成功”和“业务可用”分开:前者只说明文件能被写出,后者还要检查必须存在的 manifest.json。生产环境可根据业务增加文件大小上限、总解压大小上限和成员数量上限,避免一个合法格式的压缩包耗尽磁盘。

常见问题:zipfile 解压的几个边界

文件后缀是 zip,为什么 is_zipfile 仍返回 False?

后缀只是文件名,is_zipfile 根据实际内容判断。常见原因是下载到了错误页、文件为空,或上传过程没有完成。

is_zipfile 返回 True,还会抛 BadZipFile 吗?

会。快速检查通过后,完整打开中央目录或读取具体成员仍可能发现截断和结构损坏,所以必须保留 BadZipFile 的异常处理。

能不能直接使用 extractall?

只有在成员路径和资源上限都已经被可信代码检查过时才考虑使用。面向用户上传的压缩包,建议逐项检查成员并写入受控目录。

为什么要先解压到临时目录?

临时目录让校验失败不会污染正式目录,也能避免其他请求看到一半的文件集合。完成清单检查后再切换,回滚边界更清楚。

发布前的最小检查清单

  • 记录真实文件大小、来源响应状态和上传任务编号,不用扩展名代替文件类型。
  • 先执行 is_zipfile,再用 with ZipFile 打开并捕获 BadZipFile
  • 逐项拒绝绝对路径、父目录片段和超出解压资源上限的成员。
  • 在临时目录完成必需文件核对,成功后才切换到正式目录。
版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go base64.Encoding.Strict 如何拦截尾部脏位:解码校验与兼容边界Go base64.Encoding.Strict 如何拦截尾部脏位:解码校验与兼容边界
上一篇
Go base64.Encoding.Strict 如何拦截尾部脏位:解码校验与兼容边界
Go crypto/tls 最小化证书轮换:GetCertificate 回调与并发读取边界
下一篇
Go crypto/tls 最小化证书轮换:GetCertificate 回调与并发读取边界
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ljg-skills -
    ljg-skills
    ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
    5369次使用
  • MELO音乐 - AI 音乐生成平台,支持多模态创作能力
    MELO音乐
    MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
    4878次使用
  • UniScribe - AI 免费在线音视频转文字平台
    UniScribe
    UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
    4826次使用
  • 剧云 - 免费 AI 智能中文剧本创作平台
    剧云
    剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
    5077次使用
  • 万象有声 - AI 一站式有声内容创作平台
    万象有声
    万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
    5035次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码