Я хочу извлечь файл из zip на определенный путь, игнорируя путь к файлу в архиве. Это очень простой в Python 2.6 (моя строка документации длиннее, чем код)Как имитировать ZipFile.open в Python 2.5?
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, но у this forum post было хорошее решение, которое использует ZipInfo.file_offset
для поиска нужной точки в zip и используйте zlib.decompressobj
, чтобы распаковать байты оттуда. К сожалению, ZipInfo.file_offset
был удален в Python 2.5!
Итак, учитывая, что все, что у нас есть в Python 2.5, это ZipInfo.header_offset
, я подумал, что мне просто нужно разобрать и пропустить структуру заголовка, чтобы получить сам файл. Использование Wikipedia как a reference (я знаю) Я придумал это намного дольше и не очень изящное решение.
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
на атрибут ZipInfo.extra
дает 4 байта меньше, что вызывает смещение неправильно вычислялся. Может, я что-то упустил?
Можно ли улучшить это решение для Python 2.5?
Edit: Я должен был сказать, очевидное решение, как было предложено ChrisAdams
dest_file.write(zip_file.read(name))
потерпит неудачу с MemoryError
для любого достаточно размера файла, содержащегося в молнии, потому что он пытается хлебать весь файл в память за один присест. У меня большие файлы, поэтому мне нужно вывести содержимое на диск.
Кроме того, обновление Python - это очевидное решение, но это полностью из моих рук и по существу невозможно.
Почему вы не можете обновить? 2,7 является последним в t он 2.x, вы совсем устарели ... нет веской причины оставаться на старой версии – Daenyth
@ Даенит Я только хочу. Попробуйте сообщить об этом операционной команде, которая управляет серверами x00 ... – Day