class StringIOzipper()

Code-Stücke können hier veröffentlicht werden.
Antworten
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

In PyLucid hab ich nun an mehreren Stellen, das man ZIP Achive erstellt und zum Download anbietet...
Darum hab ich nun mal eine allgemeine Klasse gebaut, auf die man zurück greifen kann.

Ich weiß aber nicht, ob das so optimal ist:

Code: Alles auswählen

class StringIOzipper(object):
    """
    A StringIO ZIP file maker.
    """
    def __init__(self, out_err):
        """
        out_err kann z.B. sys.stdout sein :)
        """
        import StringIO, zipfile

        self.out_err = out_err

        self._buffer = StringIO.StringIO()
        self.zip = zipfile.ZipFile(self._buffer, "w", zipfile.ZIP_DEFLATED)

    def add_file(self, arcname, data):
        """
        Fügt Dateien in's ZIP Archiv hinzu.
        """
        if not isinstance(arcname, unicode):
            self.out_err.write("Warning arcname is not unicode!")
        else:
            # Namen in ZIP-Dateien werden immer mit dem codepage 437
            # gespeichert, siehe Kommentare im Python-Bug 878120:
            #https://sourceforge.net/tracker/?func=detail&atid=105470&aid=878120&group_id=5470
            try:
                arcname = arcname.encode("cp437", "strict")
            except UnicodeError, e:
                self.out_err.write(
                    "arcname encode error: %s (Use replace error handling)" % e
                )
                arcname = arcname.encode("cp437", "replace")

        if isinstance(data, unicode):
            try:
                data = data.encode("UTF8", "strict")
            except UnicodeError, e:
                self.out_err.write(
                    "data encode error: %s (Use replace error handling)" % e
                )
                data = data.encode("UTF8", "replace")

        self.zip.writestr(arcname, data)

    def close(self):
        """
        Schließen der ZIP Datei.

        Wenn alle Dateien in die ZIP Datei geschrieben wurden, müssen wir
        die Datei schließen ist ganz wichtig, das erst dann der ZIP Header
        geschrieben wird!
        """
        self.zip.close()

        # Größe feststellen:
        self._buffer.seek(0,2) # Am Ende der Daten springen
        self._buffer_len = self._buffer.tell() # Aktuelle Position
        self._buffer.seek(0) # An den Anfang springen

    def get_StringIO(self):
        return self._buffer
    def get_len(self):
        return self._buffer_len

    def block_write(self, out_object, block_size):
        """
        Schreibt die ZIP Datei blockweise in's out-Objekt
        """
        while True:
            block = self._buffer(block_size)
            if not block:
                break
            out_object.write(block)

        # Aufräumen:
        del(self._buffer)

    def debug(self, out):
        """
        out kann z.B. sys.stdout sein
        """
        def calc_ratio(file_size, compress_size):
            try:
                return float(compress_size)/file_size*100
            except ZeroDivisionError:
                return 100.0

        out.write("_"*79)
        out.write("StringIOzipper Debug:")
        out.write("-"*79)
        total_compress_size = 0
        for fileinfo in self.zip.infolist():
            filename = fileinfo.filename
            if filename.endswith("/"):
                # Ist ein Verzeichniss
                out.write("dir: %s" % filename)
                continue

            out.write("file: %s" % fileinfo.filename)

            total_compress_size += fileinfo.compress_size
            ratio = calc_ratio(fileinfo.file_size, fileinfo.compress_size)

            out.write(
                "compress size: %s - file_size: %s (ratio: %.2f%%)" % (
                    fileinfo.compress_size, fileinfo.file_size, ratio
                )
            )

            d = fileinfo.date_time
            out.write(
                "file date: %0.2i.%0.2i.%i %0.2i:%0.2i:%0.2i" % (
                    d[2],d[1],d[0],d[3],d[4],d[5]
                )
            )
            out.write("-"*79)

        out.write("zip len: %s" % self._buffer_len)
        out.write("total compress size: %s" % total_compress_size)

        ratio = calc_ratio(self._buffer_len, total_compress_size)
        out.write("total ratio: %.2f%%" % ratio)

        out.write("-"*79)
Den import im __init__ hab ich eigentlich extra gemacht, damit nicht die Module immer importiert werden, auch wenn im aktuellen request die Klasse überhaupt nicht benutzt wird.

Mir fehlt auch noch die Möglichkeit die Kompressionsstufe anzugeben. Eigentlich kann man beim zlib module ja ein level von 0 bin 9 angeben. Wie kann man das beim zipfile machen???

Ich bitte um Verbesserungsvorschläge!

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, hab den obrigen Code mal erneuert... Im alten steckten noch so einige Fehler, aber der war auch ungetestet.
Außerdem hab ich nun eine debug() Methode eingebaut. Damit kann man nach der erstellung der ZIP, gleich nachschauen, welche Daten erhalten sind...

Was ich noch ändern will:

Bei zipfile.writestr kann man auch, statt nur dem Namen, ein ZipInfo Instanz übergeben: http://docs.python.org/lib/module-zipfile.html#l2h-2854

Damit kann man dann das Dateidatum in der ZIP Datei bestimmen...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten