cgigz.py; CGI Seite komprimiert mit gzip versenden

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:

Also ich hab mir hier ein gebastelt:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: ISO-8859-1 -*-

import os, sys

class ZLIBout:
    def __init__( self ):
        from zlib import compress
        self.compress = compress

    def write( self, txt ):
        sys.stdout.write( self.compress(txt) )


class Out:
    def __init__( self ):
        print "Content-Type: text/html"
        self.MyStdOut = sys.stdout
        self.mode = self.detect_mode()
        print 'Content-Encoding: %s\n' % self.mode
        self.set_mode()

    def write( self, txt ):
        self.MyStdOut.write( txt )

    def detect_mode( self ):
        "Ermittle den möglichen Modus"
        if os.environ.has_key('HTTP_ACCEPT_ENCODING'):
            modes = os.environ['HTTP_ACCEPT_ENCODING'].split(',')
            if "gzip" in modes:
                return "gzip"
            elif "deflate" in modes:
                return "deflate"
        return "flat"

    def set_mode( self ):
        if self.mode == "gzip":
            from gzip import GzipFile
            sys.stdout = GzipFile(mode='wb', fileobj=sys.stdout)
        elif self.mode == "deflate":
            MyZLIBout = ZLIBout()
            sys.stdout = MyZLIBout

    def get_mode( self ):
        return self.mode




if __name__ == "__main__":
    os.environ['HTTP_ACCEPT_ENCODING'] = "deflate"
    MyOut = Out()
    print "Das ist ein toller Test Text!"
    print MyOut.get_mode
"gzip" und "flat" funktioniert einwandfrei...
Leider bietet zlib kein fileobj... Zwar gibt es da dieses compressobj() aber das scheind mit was anderes zu sein... Deshalb hab ich dafür auch noch mal eine Klasse geschrieben, die eigentlich das selbe machen sollte wie es gzip.GzipFile() kann...
Aber es gibt ein endlos Loop beim schreiben in ZLIBout.write():

Code: Alles auswählen

Traceback (most recent call last):
  File "CompressedOut.py", line 84, in ?
    print "Das ist ein toller Test Text!"
  File "CompressedOut.py", line 13, in write
    print self.compress(txt)
  File "CompressedOut.py", line 13, in write
    print self.compress(txt)
[...]
Ein seperater Test funktioniert aber:

Code: Alles auswählen

    MyZLIBout = ZLIBout()
    MyZLIBout.write("Noch ein Test...")
BlackJack

jens hat geschrieben:Aber es gibt ein endlos Loop beim schreiben in ZLIBout.write():

Code: Alles auswählen

Traceback (most recent call last):
  File "CompressedOut.py", line 84, in ?
    print "Das ist ein toller Test Text!"
  File "CompressedOut.py", line 13, in write
    print self.compress(txt)
  File "CompressedOut.py", line 13, in write
    print self.compress(txt)
[...]
Irgendwie glaube ich Du hast nicht den Quelltext gepostet, der diese Ausgabe produziert. Ich sehe in Zeile 13 nämlich kein ``print self.compress(txt)``.

Aber auch das `sys.stdout.write()` wird nicht funktionieren. Du hast sys.stdout doch ersetzt. Alles was dort hingeschrieben wird, wird komprimiert. Also auch das Ergebnis von `self.compress()`. Damit wird wieder Dein `ZLIBout.write()` aufgerufen. Und das schreibt wieder auf `sys.stdout` und ... naja immer so weiter.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, jetzt hab ich's aber! Ich habe nun die ZlibFile so gebastelt, das sie genauso wie gzip.GzipFile() funktioniert:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: ISO-8859-1 -*-

import os, sys

class ZlibFile:
    def __init__( self, fileobj ):
        self.fileobj = fileobj
        from zlib import compress
        self.compress = compress

    def write( self, txt ):
        self.fileobj.write( self.compress(txt) )

    def flush( self ):
        self.fileobj.flush()

class AutoCompressedOut:
    def __init__( self ):
        print "Content-Type: text/html"
        self.mode = self.detect_mode()
        print 'Content-Encoding: %s\n' % self.mode
        self.set_mode()

    def detect_mode( self ):
        "Ermittle den möglichen Modus"
        if os.environ.has_key('HTTP_ACCEPT_ENCODING'):
            modes = os.environ['HTTP_ACCEPT_ENCODING'].split(',')
            if "gzip" in modes:
                return "gzip"
            elif "deflate" in modes:
                return "deflate"
        return "flat"

    def set_mode( self ):
        if self.mode == "gzip":
            from gzip import GzipFile
            sys.stdout = GzipFile( mode='wb', fileobj=sys.stdout )
        elif self.mode == "deflate":
            sys.stdout = ZlibFile( fileobj=sys.stdout )

    def get_mode( self ):
        return self.mode

if __name__ == "__main__":
    #~ os.environ['HTTP_ACCEPT_ENCODING'] = "deflate"
    os.environ['HTTP_ACCEPT_ENCODING'] = "gzip"
    MyOut = AutoCompressedOut()

    print "Das ist ein toller Test Text!"
    print "Und noch eine Zeile..."
    print "...und noch was..."
    print "Verwendeter Modus: '%s'" % MyOut.get_mode()
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Leider muß ich nun feststellen, die komprimierte Übertragung hat einen Nachteil bei langen Seiten! Denn der Browser zeigt mit immer erst die Seite an, wenn sie komplett geladen ist und nicht schon die ersten Teile einer Tabelle...

Code: Alles auswählen

if __name__ == "__main__":
    #~ os.environ['HTTP_ACCEPT_ENCODING'] = "deflate"
    os.environ['HTTP_ACCEPT_ENCODING'] = "gzip"
    MyOut = AutoCompressedOut()

    import time
    for i in xrange(10):
        print i
        time.sleep(0.1)
Mit diesem Test kann man sehen, das gzip auch wirklich erst die Daten raushaut, wenn die Schleife fertig ist. (ein sys.stdout.flush() ändert da auch nix).
deflate kann es allerdings! Dennoch beginnt der Browser erst mit der Dekompression, wenn die Seite ganz da ist :twisted:

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

Die Testschleife ist ein bischen zu klein. Da werden sicher ein paar KB gepuffert. Ansonsten schreibt GzipFile die komprimierten Daten sofort auf das darunterliegende Dateiobjekt.

Und deflate muss doch erst die Daten komplett haben um sie komprimieren zu können. Wie kann da vorher schon etwas geschickt werden?

Ob die Seite während des Empfangs schon entpackt und angezeigt wird, ist ein Problem des Browsers. Technisch möglich ist es zumindest bei gzip jedenfalls.
Antworten