我想将文件从 zip 中提取到特定路径,忽略存档中的文件路径。这在 Python 2.6 中非常简单(我的文档字符串比代码长)
import shutil
import zipfile
def extract_from_zip(name, dest_path, zip_file):
"""Similar to zipfile.ZipFile.extract but extracts the file given by name
from the zip_file (instance of zipfile.ZipFile) to the given dest_path
*ignoring* the filename path given in the archive completely
instead of preserving it as extract does.
"""
dest_file = open(dest_path, 'wb')
archived_file = zip_file.open(name)
shutil.copyfileobj(archived_file, dest_file)
extract_from_zip('path/to/file.dat', 'output.txt', zipfile.ZipFile('test.zip', 'r'))
但在 Python 2.5 中,ZipFile.open方法不可用。我在 stackoverflow 上找不到解决方案,但是这个论坛帖子有一个很好的解决方案,利用ZipInfo.file_offset寻找 zip 中的正确点并使用zlib.decompressobj从那里解压字节。很遗憾ZipInfo.file_offset
在 Python 2.5 中被删除了!
因此,考虑到 Python 2.5 中我们所拥有的就是ZipInfo.header_offset,我想我只需要解析并跳过标头结构即可自己获取文件偏移量。使用维基百科作为一个参考(我知道)我想出了这个更长而且不是很优雅的解决方案。
import zipfile
import zlib
def extract_from_zip(name, dest_path, zip_file):
"""Python 2.5 version :("""
dest_file = open(dest_path, 'wb')
info = zip_file.getinfo(name)
if info.compress_type == zipfile.ZIP_STORED:
decoder = None
elif info.compress_type == zipfile.ZIP_DEFLATED:
decoder = zlib.decompressobj(-zlib.MAX_WBITS)
else:
raise zipfile.BadZipFile("Unrecognized compression method")
# Seek over the fixed size fields to the "file name length" field in
# the file header (26 bytes). Unpack this and the "extra field length"
# field ourselves as info.extra doesn't seem to be the correct length.
zip_file.fp.seek(info.header_offset + 26)
file_name_len, extra_len = struct.unpack("<HH", zip_file.fp.read(4))
zip_file.fp.seek(info.header_offset + 30 + file_name_len + extra_len)
bytes_to_read = info.compress_size
while True:
buff = zip_file.fp.read(min(bytes_to_read, 102400))
if not buff:
break
bytes_to_read -= len(buff)
if decoder:
buff = decoder.decompress(buff)
dest_file.write(buff)
if decoder:
dest_file.write(decoder.decompress('Z'))
dest_file.write(decoder.flush())
请注意我如何解压并读取给出额外字段长度的字段,因为调用len
on the ZipInfo.extra
属性少了 4 个字节,从而导致偏移量计算不正确。也许我在这里遗漏了一些东西?
有人可以针对 Python 2.5 改进这个解决方案吗?
Edit:我应该说,克里斯亚当斯建议的显而易见的解决方案
dest_file.write(zip_file.read(name))
将会失败MemoryError
对于 zip 中包含的任何大小合理的文件,因为它试图一次性将整个文件放入内存中。我有大文件,所以我需要将内容流式传输到磁盘。
另外,升级 Python 是一种显而易见的解决方案,但它完全超出了我的控制范围,而且基本上是不可能的。