Wie simuliere ich ZipFile.open in Python 2.5?
Ich möchte eine Datei aus einer ZIP-Datei in einen bestimmten Pfad extrahieren und dabei den Dateipfad im Archiv ignorieren. Dies ist in Python 2.6 sehr einfach (mein Docstring ist länger als der Code)
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'))
Aber in Python 2.5, The ZipFile.open -Methode ist nicht verfügbar. Ich konnte keine Lösung für den Stackoverflow finden, aberdieser Forumsbeitrag hatte eine schöne Lösung, die das @ nutZipInfo.file_offset
, um nach der richtigen Stelle im Zip zu suchen und @ zu verwendzlib.decompressobj
, um die Bytes von dort zu entpacken. UnglücklicherweiseZipInfo.file_offset
wurde in Python 2.5 entfernt!
So, da wir in Python 2.5 nur das @ habZipInfo.header_offset
, Ich dachte, ich müsste nur die Header-Struktur analysieren und überspringen, um selbst zum Datei-Offset zu gelangen. Wikipedia als @ verwendeine Referen (Ich weiß) Ich habe mir diese viel längere und nicht sehr elegante Lösung ausgedacht.
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())
Note, wie ich das Feld auspacke und lese, das die Länge des zusätzlichen Feldes angibt, weil der Aufruf vonlen
auf derZipInfo.extra
-Attribut ergibt 4 Bytes weniger, wodurch der Offset falsch berechnet wird. Vielleicht fehlt mir hier etwas?
Kann jemand diese Lösung für Python 2.5 verbessern?
Bearbeiten Ich hätte sagen sollen, die naheliegende Lösung wie von ChrisAdams vorgeschlagen
dest_file.write(zip_file.read(name))
wird fehlschlagen mitMemoryError
für alle Dateien mit einer angemessenen Größe, die in der ZIP-Datei enthalten sind, da versucht wird, die gesamte Datei auf einmal in den Speicher zu kopieren. Ich habe große Dateien, daher muss ich den Inhalt auf die Festplatte streamen.
Auch ein Upgrade von Python ist die naheliegende Lösung, aber eine, die völlig aus meiner Hand liegt und im Grunde genommen unmöglich ist.