Seite 1 von 1

Problem mit zipfile

Verfasst: Dienstag 5. September 2006, 10:10
von HarryH
Hallo,

Ich habe ein Problem mit dem Modul zipfile und großen Dateien in Ziparchivs (> 500MB)
Beim lesen dieser Dateien mit der Methode read() erhalte ich folgenden Fehler:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Dokumente und Einstellungen\hh16534\Eigene Dateien\Python\Module\hhZip.py", line 219, in Unzip
    dezip = self.zipfile.read(self.name_dic[filename])
  File "d:\python24\lib\zipfile.py", line 357, in read
    bytes = dc.decompress(bytes)
MemoryError
Anscheinend läuft der Speicher voll?
Wie kann ich diesen Fehler beheben?

Verfasst: Dienstag 5. September 2006, 20:09
von BlackJack
Gar nicht. Oder zumindest nicht ohne das `zipfile` Modul zu erweitern. Das liest immer nur komplette Dateien ein. Was ungünstig wird, wenn die Datei gross ist.

re:

Verfasst: Mittwoch 6. September 2006, 07:30
von HarryH
Hallo,

Ich bin mittlerweile auch darauf gekommen, das es nur dadurch möglich ist das zifpile-Modul zu erweitern.
Ich habe die Methode 'zipfile.read' etwas umgebaut.
Folgende Methode kann in eine geerbte Klasse von zipfile.ZipFile übernommen werden. Sie dient dazu, auch große Files eines Archivs mühelos zu entpacken. Der File-Buffer wird dabei nicht komplett dekomprimiert sondern schrittweise.

Code: Alles auswählen

def unzip(self, name, dir):
    """Unzip file and write to defined directory"""
    #Checking
    if self.mode not in ("r", "a"):
        raise RuntimeError, 'read() requires mode "r" or "a"'
    if not self.fp:
        raise RuntimeError, \
              "Attempt to read ZIP archive that was already closed"
    #Filename and dir creation
    fname = os.path.join(dir, os.path.basename(name))
    if not os.path.exists(dir):
        os.makedirs(dir)

    #Jump to right archiv position and set bufsize
    zinfo = self.getinfo(name)
    filepos = self.fp.tell()
    self.fp.seek(zinfo.file_offset, 0)
    if zinfo.compress_size > 1024:
        bufsize = zinfo.compress_size / 50
    else:
        bufsize = zinfo.compress_size

    #Start unzip operation
    if zinfo.compress_type == zipfile.ZIP_STORED:
        #Read bytes from zip archiv name and writing
        crc = 0
        f = file(fname, "wb")
        while f.tell() < zinfo.compress_size:
            if bufsize > zinfo.compress_size - f.tell():
                bufsize = zinfo.compress_size - f.tell()
            bytes = self.fp.read(bufsize)
            f.write(bytes)
            crc = binascii.crc32(bytes, crc)
        self.fp.seek(filepos, 0)
        f.close()
    elif zinfo.compress_type == zipfile.ZIP_DEFLATED:
        #Read bytes from zip archiv name
        bytes = self.fp.read(zinfo.compress_size)
        self.fp.seek(filepos, 0)
        #Checking
        if not zlib:
            raise RuntimeError, \
                  "De-compression requires the (missing) zlib module"
        #Decopmressing and writing
        dc = zlib.decompressobj(-15)
        crc = 0
        f = file(fname, "wb")
        while 1:
            bytes = dc.decompress(bytes, bufsize)
            f.write(bytes)
            crc = binascii.crc32(bytes, crc)
            if dc.unconsumed_tail:
                bytes = dc.unconsumed_tail
            else:
                break
        #Need to feed in unused pad byte so that zlib won't choke
        ex = dc.decompress('Z') + dc.flush()
        if ex:
            f.write(ex)
            crc = binascii.crc32(ex, crc)
        f.close()
    else:
        raise zipfile.BadZipfile, \
              "Unsupported compression method %d for file %s" % \
        (zinfo.compress_type, name)
    #Check CRC
    if crc != zinfo.CRC:
        raise zipfile.BadZipfile, "Bad CRC-32 for file %s" % name

    #Set orginal file date anf file attribute
    mtime = time.mktime(zinfo.date_time + (-1, -1, -1))
    os.utime(fname, (time.time(), mtime))
Vielleicht hat ja jemand noch Verbesserungsvorschläge!?

Re: re:

Verfasst: Mittwoch 6. September 2006, 11:22
von Leonidas
HarryH hat geschrieben:Vielleicht hat ja jemand noch Verbesserungsvorschläge!?
Hast du es auch mit Monkey-Patching in die Originalklasse versucht statt eine Klasse abzuleiten?

re:

Verfasst: Mittwoch 6. September 2006, 11:27
von HarryH
Hi Leonidas,

Was ist Monkey-Patching? Das habe ich noch nie gehört :oops:

Re: re:

Verfasst: Mittwoch 6. September 2006, 11:34
von Leonidas
HarryH hat geschrieben:Was ist Monkey-Patching? Das habe ich noch nie gehört :oops:
Wird hier am Beispiel von Zope erklärt.

In deinem Fall würe das etwa so aussehen:

Code: Alles auswählen

import zipfile
zipfile.ZipFile.unzip = unzip
Oder du kannst read() überschrieben, dass es nicht an MemoryErrors leidet und auch wieder mit Monkey Patching arbeiten.

Achja, manche meinen das Monkey Patching unsauber ist - aber ich finde es eigentlich in Massen eigentlich ganz akzeptabel.

re:

Verfasst: Mittwoch 6. September 2006, 11:46
von HarryH
Hi,

Bedeutet

Code: Alles auswählen

zipfile.ZipFile.unzip = unzip
das der Klasse zipfile.ZipFile die Methode unzip hinzugefügt wird?

Wenn ja, was ist der bedeutende Unterschied dazu, wenn ich von zipfile.ZipFile erbe?

Re: re:

Verfasst: Donnerstag 7. September 2006, 10:12
von Leonidas
Je, genau, das bedeutet es.
HarryH hat geschrieben:Wenn ja, was ist der bedeutende Unterschied dazu, wenn ich von zipfile.ZipFile erbe?
In diesem Fall ist der Unterschied nicht so relevant. Aber stell dir vor, du modifizierst read() so, dass es out-of-the-box mit großen ZIP-Dateien auskommt. Dann injizierst du diesen Monkey-Patch in die ZipFile-Klasse. Und sofort können alle Skripte durch diesen Monkey-Patch _sofort_ mit großen ZIP-Dateien umgehen, ohne dass man noch irgendetwas ändern müsste.

Verfasst: Donnerstag 7. September 2006, 15:22
von BlackJack
Das klingt ein bischen übertrieben. Nicht alle Skripte sondern nur alle Module in Programmen die vor dem Benutzen des `zipfile` Moduls den Monkey-Patch importiert haben.