使用 ZipFile 模块从 zipfile 中删除文件

Posted

技术标签:

【中文标题】使用 ZipFile 模块从 zipfile 中删除文件【英文标题】:Delete file from zipfile with the ZipFile Module 【发布时间】:2009-02-04 23:00:26 【问题描述】:

我想从压缩文件中删除文件的唯一方法是创建一个临时压缩文件而不删除要删除的文件,然后将其重命名为原始文件名。

在 python 2.4 中,ZipInfo 类有一个属性file_offset,因此可以创建第二个 zip 文件并将数据复制到其他文件而无需解压缩/重新压缩。

这个file_offset 在 python 2.6 中丢失了,那么除了通过解压缩每个文件然后重新压缩它来创建另一个 zipfile 之外,还有其他选择吗?

是否有直接删除zipfile中文件的方法,我搜索并没有找到任何东西。

【问题讨论】:

我在 Python 错误跟踪器上发现了这个线程,讨论从 zip 文件中删除文件的困难:bugs.python.org/issue6818 【参考方案1】:

以下 sn-p 对我有用(从 Zip 存档中删除所有 *.exe 文件):

zin = zipfile.ZipFile ('archive.zip', 'r')
zout = zipfile.ZipFile ('archve_new.zip', 'w')
for item in zin.infolist():
    buffer = zin.read(item.filename)
    if (item.filename[-4:] != '.exe'):
        zout.writestr(item, buffer)
zout.close()
zin.close()

如果您将所有内容都读入内存,则可以消除对第二个文件的需要。但是,这个 sn-p 会重新压缩所有内容。

经过仔细检查,ZipInfo.header_offset 是从文件开始的偏移量。名称具有误导性,但主 Zip 标头实际上存储在文件末尾。我的十六进制编辑器证实了这一点。

因此您将遇到的问题如下:您还需要删除主标题中的目录条目,否则它将指向一个不再存在的文件。如果您也保留要删除的文件的本地标头,则保持主标头完整可能会起作用,但我不确定。您是如何使用旧模块的?

在不修改主标题的情况下,当我打开它时,我得到一个错误“zipfile 中缺少 X 个字节”。 This 可能会帮助您了解如何修改主标题。

【讨论】:

谢谢,但如果我没记错的话 - 当您查看 zipfile.writestr 时,您会发现这只是重新压缩。直接复制已经压缩的文件而不解压缩然后再次压缩会快得多。 @RSabt 我同意 mdm 的观点,即 unzip-and-rezip 是迄今为止唯一可行的选择。顺便说一句,mdm 的代码有帮助,但是当你要做更认真的事情时,最好使用 os.path.splitext()。 您也可以避免提取可执行文件。首先检查名称,如果不是可执行文件,则读取输入。会节省一些无用的提取时间。【参考方案2】:

不是很优雅,但我就是这样做的:

import subprocess
import zipfile

z = zipfile.ZipFile(zip_filename)

files_to_del = filter( lambda f: f.endswith('exe'), z.namelist()]

cmd=['zip', '-d', zip_filename] + files_to_del
subprocess.check_call(cmd)

# reload the modified archive
z = zipfile.ZipFile(zip_filename)

【讨论】:

这就是我最终要做的。丑陋,但ZipFile 似乎没有删除或更新/替换文件的方法。【参考方案3】:

ruamel.std.zipfile¹ 中的例程 delete_from_zip_file 允许您根据 ZIP 中的完整路径或基于 (re) 模式删除文件。例如。您可以使用

test.zip 中删除所有.exe 文件
from ruamel.std.zipfile import delete_from_zip_file

delete_from_zip_file('test.zip', pattern='.*.exe')  

(请注意*之前的点)。

这与 mdm 的解决方案类似(包括需要重新压缩),但在内存中重新创建 ZIP 文件(使用 InMemZipFile() 类),在完全读取后覆盖旧文件。


¹ 免责声明:我是该软件包的作者。

【讨论】:

delete_from_zip_file 例程对我来说非常有用,但我在尝试从包含一堆文件夹的大存档(大小约为 3Gb)中删除许多文件时遇到此错误:“LargeZipFile:Zipfile 大小会需要 ZIP64 扩展”。我猜应该在 init.py 文件中的 ruamel.std.zipfile 中进行修改(比如 zipfile.ZipFile(..) 的 allowZip64 = True),对吧? 我从未与allowZip64 合作过,不知道它是关于什么的。【参考方案4】:

基于Elias Zamaria对该问题的评论。

阅读完https://bugs.python.org/issue6818,我想提供有关它的更新。

今天,解决方案已经存在,但由于缺少作者的贡献者协议而没有被 Python 批准。

不过,您可以从https://github.com/python/cpython/blob/659eb048cc9cac73c46349eb29845bc5cd630f09/Lib/zipfile.py 获取代码并从中创建一个单独的文件。之后,只需从您的项目而不是内置的 python 库中引用它:import myproject.zipfile as zipfile

用法:

with zipfile.ZipFile(f"archive.zip", "a") as z:
    z.remove(f"firstfile.txt")

我相信它会包含在未来的 python 版本中。对我来说,它就像给定用例的魅力。

【讨论】:

以上是关于使用 ZipFile 模块从 zipfile 中删除文件的主要内容,如果未能解决你的问题,请参考以下文章

在 Spark 中的 EMR 上使用 --py-files 从 .zip 文件(使用 zipfile 包在 python 中创建)导入模块时出现问题

python模块 zipfile

Python 和 zipfile 模块

zipfile模块

python3中zipfile模块的常用方法

Python之zipfile模块的使用