You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:Python zipfile模块为何仅提取单个文件?

解决Python更新器提取ZIP文件仅提取一个就停止的问题

问题核心原因

你的代码中途停止提取主要有三个问题:

  1. 未处理文件覆盖异常:zipfile.ZipFile.extract()默认不会覆盖已存在的文件,遇到已有文件时会直接抛出FileExistsError,导致程序终止。
  2. UI线程阻塞:循环提取文件时会卡住wxPython的UI线程,进度对话框假死,甚至被系统判定为无响应而中断。
  3. 无效项干扰:ZIP的namelist()会包含目录条目(比如subdir/),提取这些目录不仅没用,还会打乱进度计数。

修复后的代码

import wx
from zipfile import ZipFile, is_zipfile
import os

def extract_update():
    zip_path = 'wotu.zip'
    # 先校验ZIP文件有效性
    if not is_zipfile(zip_path):
        wx.MessageBox("无效的ZIP文件!", "错误", wx.OK | wx.ICON_ERROR)
        return

    with ZipFile(zip_path, 'r') as f:
        # 过滤掉updater.exe和目录项,只保留实际文件
        files = [file for file in f.namelist() 
                 if file != 'updater.exe' and not file.endswith('/')]
        total_files = len(files)
        
        if total_files == 0:
            wx.MessageBox("没有需要提取的文件!", "提示", wx.OK | wx.ICON_INFORMATION)
            return

        # 创建带取消按钮的进度对话框
        dlg = wx.ProgressDialog('更新中', '正在提取文件...', total_files, 
                                style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_CAN_ABORT)
        progress = 0
        keep_running = True

        for file in files:
            if not keep_running:
                break

            try:
                target_path = os.path.abspath(file)
                # 确保目标文件的父目录存在
                os.makedirs(os.path.dirname(target_path), exist_ok=True)
                # 先提取到临时文件,再替换原文件实现覆盖
                temp_file = target_path + '.tmp'
                f.extract(file, os.path.dirname(temp_file))
                os.replace(temp_file, target_path)
                
                print(f"已提取:{file}")
                progress += 1
                # 更新进度,同时检查用户是否点击取消
                keep_running, _ = dlg.Update(progress, f"正在提取:{file}")
                # 处理UI事件,防止对话框假死
                wx.Yield()
            except Exception as e:
                print(f"提取{file}失败:{str(e)}")
                # 询问用户是否继续提取其他文件
                keep_running = wx.MessageBox(f"提取{file}失败\n错误信息:{str(e)}\n是否继续提取?", 
                                          "警告", wx.YES_NO | wx.ICON_WARNING) == wx.YES
                if not keep_running:
                    break

        dlg.Destroy()
        wx.MessageBox("更新完成!", "成功", wx.OK | wx.ICON_INFORMATION)

# 主线程调用示例
if __name__ == '__main__':
    app = wx.App(False)
    extract_update()
    app.MainLoop()

关键修改说明

  • 强制覆盖文件:通过「临时文件提取+替换」的方式绕开extract()的覆盖限制,确保旧文件能被替换。
  • 过滤无效条目:只处理ZIP中的实际文件,跳过updater.exe和目录项。
  • UI响应优化:加入wx.Yield()处理UI事件,避免进度对话框假死;同时监听用户的取消操作,允许中途终止。
  • 异常容错:捕获提取过程中的所有异常,让用户选择是否继续,避免程序直接崩溃。

内容的提问来源于stack exchange,提问作者Blindgoofball

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:11:20