cgigz.py; CGI Seite komprimiert mit gzip versenden

Code-Stücke können hier veröffentlicht werden.
XT@ngel
User
Beiträge: 256
Registriert: Dienstag 6. August 2002, 14:36
Kontaktdaten:

Donnerstag 3. Februar 2005, 14:15

Das ist die erste Version meine stdout -> gzip klasse.
Auf tempfile hab ich verzichtet, da es unter Windows nicht richtig arbeitet.
Ziel war eigentlich eine benutzung wie bei PHP, was ich bis jetzt noch nicht hinbekommen habe.
Deshalb auch die verwendung eines Destruktors, wo ich es Python überlassen das Object am Ende des scripts zu löschen.
Wems nicht gefällt kann es sich ja umschreiben :mrgreen:

Code: Alles auswählen

__version__ = '0.1'

import sys, gzip, time, os

class NotSupportedEncoding(Exception):
    pass

class enable:
    def __init__(self):
        Encodings = os.environ['HTTP_ACCEPT_ENCODING'].split(',')
        if 'gzip' in Encodings:
            self.TMP_File = "%s.cgigz" % str(time.time())
            self.GZIP  = gzip.GzipFile(filename=self.TMP_File, mode='wb')
            sys.stdout = self.GZIP
        else:
            raise NotSupportedEncoding

    def __del__(self):
        sys.stdout = sys.__stdout__
        self.GZIP.close() 
        send = open(self.TMP_File, 'rb')
        print send.read()
        send.close()
        os.remove(self.TMP_File)
Beispiel

Code: Alles auswählen

import cgigz

try:
    print "Content-Type: text/html"
    print "Content-Encoding: gzip"
    print
    garbage_collection_dummy = cgigz.enable()
except cgigz.NotSupportedEncoding:
    print "Content-Type: text/html"
    print

print "hallo"
print "TETFSDHSDGHGHSD"
Zuletzt geändert von XT@ngel am Freitag 4. Februar 2005, 04:39, insgesamt 1-mal geändert.
Benutzeravatar
jens
Moderator
Beiträge: 8461
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Donnerstag 3. Februar 2005, 16:12

cgigz hab ich garnicht ?!?! Oder ist damit dein erster codebrocken gemeint???

Hier mal meine Variante... Die aber nicht funktioniert :( Warum???

Code: Alles auswählen

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

import os,zlib

def putHTML( HTMLdata ):
    if "gzip" in os.environ['HTTP_ACCEPT_ENCODING'].split(','):
        print "Content-Type: text/html"
        print "Content-Encoding: gzip"
        print
        print zlib.compress( HTMLdata )
    else:
        #~ print "Content-Encoding: deflate"
        print "Content-Type: text/html\n"
        print HTMLdata



if __name__=="__main__":
    os.environ['HTTP_ACCEPT_ENCODING'] = "gzip,deflate"
    #~ os.environ['HTTP_ACCEPT_ENCODING'] = "deflate"
    putHTML("<html><body><h1>TEST</h1></body></html>")
Ich sehe nicht genau, warum es nicht geht... Auch wenn man die Zeile mit print "Content-Encoding: deflate" auskommentiert, zeigt der Browser nix mehr an :(

Mit deiner Variante bleibt allerdings die Seite auch leer...

Ob zlib nicht ganz das richtige liefert???
XT@ngel
User
Beiträge: 256
Registriert: Dienstag 6. August 2002, 14:36
Kontaktdaten:

Donnerstag 3. Februar 2005, 18:06

cgigz hab ich garnicht ?!?! Oder ist damit dein erster codebrocken gemeint???
Ja, sorry für die schlechte Erklärung.
Mit deiner Variante bleibt allerdings die Seite auch leer...
Bei mir funktionierts einwandfrei. (Win 2000 prof. & Apache 2.0 , Python2.4 als CGI)
Vielleicht liegts an einem Copy/Paste fehler...
Wenn die Seite leer bleibt ist es meistens ein fehlerhaft gesendeter Header.
evtl. hilft dir auch die error log deines servers.

zlib werd ich mir mal genauer anschaun...
BlackJack

Freitag 4. Februar 2005, 03:01

Wenn ich die erste Methode richtig verstanden habe, dann basiert die darauf, dass das senden der Daten in der __del__() Methode stattfindet?

Das ist IMHO nicht besonders empfehlenswert. Lies Dir mal durch, was Python für Garantien gibt wann __del__() ausgeführt wird, bzw. was alles nicht als gegeben angesehen werden kann.

Wenn das Skript zuende ist, dann werden nach und nach die Objekte beseitigt und dabei eventuell vorhandene __del__() Methoden aufgerufen. Ich weiss zum Beispiel nicht, ob das Objekt sys.__stdout__ noch existieren muss, wenn Deine Methode ausgeführt wird!? Von __del__() sollte man eigentlich die Finger lassen wenn es nicht zwingend notwendig ist.

Die Funktion os.tmpfile() würde übrigens schon funktionieren. Was nicht geht, ist das erneute öffnen, da die Datei keinen Namen besitzt. Aber man kann vor dem Auslesen mit seek() wieder an den Anfang der Datei gehen.
XT@ngel
User
Beiträge: 256
Registriert: Dienstag 6. August 2002, 14:36
Kontaktdaten:

Freitag 4. Februar 2005, 04:19

Hi BlackJack,
danke für deinen Hinweis auf os.tmpfile.
Ich hatte die ganze Zeit die Funktionen aus dem Modul tempfile verwendet
und da konnt ich mit seek() rumspielen wie ich wollte, ohne Ergebnis.
Dagegen ist os.tmpfile fast ein Wunder.
Wäre dankbar für ein Beispiel (tempfile) unter Windows2000 :)
Lies Dir mal durch, was Python für Garantien gibt..
Ich habs mir sogar extra nochmal durchgelesen und da ich wusste, dass es Kritik geben wird, schrieb ich...
XT@ngel hat geschrieben:Wems nicht gefällt kann es sich ja umschreiben :mrgreen:
Vielleicht hätte ich schreiben sollen, wieso es einem nicht gefallen sollte. :roll:

Code: Alles auswählen

__author__ = 'python-forum.de'
__version__ = '0.3'

import sys, gzip, os

class NotSupportedEncoding(Exception):
    pass

class ContentAlreadySend(Exception):
    pass

class enable:
    def __init__(self):
        if 'gzip' in os.environ['HTTP_ACCEPT_ENCODING'].split(','):
            self.Send = False
            self.TMP_File = os.tmpfile()
            self.GZIP  = gzip.GzipFile(fileobj=self.TMP_File)
            sys.stdout = self.GZIP
        else:
            raise NotSupportedEncoding

    def send(self):
        if self.Send:
            raise ContentAlreadySend
        else:
            sys.stdout = sys.__stdout__
            self.GZIP.close() 
            self.TMP_File.seek(0)
            print self.TMP_File.read()
            self.TMP_File.close()
            self.Send = True
       
BlackJack

Samstag 5. Februar 2005, 00:11

Mir gefällt's immer noch nicht, darum hab' ich was ähnliches geschrieben. :-)

Ohne eine extra Klasse und ohne temporäre Datei, weil man ganz einfach `sys.stdout` in ein `GzipFile` "einwickeln" kann.

Code: Alles auswählen

import os, sys
from gzip import GzipFile

def is_supported():
    """Checks if *gzip* compression is supported by client side."""
    try:
        return 'gzip' in os.environ['HTTP_ACCEPT_ENCODING'].split(',')
    except KeyError:
        return False

def enable():
    """Enables gzip compression of `sys.stdout` if client side
    supports it.
    
    :attention: Must be called *after* all HTTP header lines are
        written as `enable()` sends the separator line between headers
        and content!
    """
    if is_supported():
        print 'Content-Encoding: gzip'
        print
        sys.stdout = GzipFile(mode='wb', fileobj=sys.stdout)
    else:
        print


#
# Test code.
#
print 'Content-Type: text/html'
enable()

print ('<html><body><h1>Compression test</h1>'
       '<p>Compression support: %s</p>'
       '</body></html>' % is_supported())
BlackJack

Samstag 5. Februar 2005, 00:17

Aus dem allerersten Beitrag:
XT@ngel hat geschrieben:

Code: Alles auswählen

try:
    print "Content-Type: text/html"
    print "Content-Encoding: gzip"
    print
    garbage_collection_dummy = cgigz.enable()
except cgigz.NotSupportedEncoding:
    print "Content-Type: text/html"
    print
Falls die Ausnahme ausgelöst wird, weil die Komprimierung nicht unterstützt wird, ist schon das "Content-Encoding: gzip" geschrieben worden. :?
BlackJack

Samstag 5. Februar 2005, 00:23

jens hat geschrieben:Ob zlib nicht ganz das richtige liefert???
Du hast das "falsche Etikett draufgeklebt". Du sendest das Ergebnis von zlib.compress(), sagst dem Browser aber vorher, da kommen `gzip` Daten. Du müsstest `deflate` ankündigen.
Benutzeravatar
jens
Moderator
Beiträge: 8461
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Montag 7. Februar 2005, 18:45

Jep, mit "deflate" klappt es... Jetzt bin ich allerdings ein wenig verwirrt... Worin besteht der Unterschied zwischen gzip und deflate ??? Welches ist besser?

Hab das gefunden:
gzip basiert auf dem deflate-Algorithmus, der eine Kombination aus LZ77 und Huffman-Kodierung ist. deflate wurde als Reaktion auf die Patente entwickelt, die auf LZW und andere Kompressionsalgorithmen bestanden.

Um die Entwicklung von Software zu vereinfachen, die Datenkompression nutzt, wurde die zlib-Bibliothek geschrieben. Sie unterstützt das gzip-Dateiformat und die deflate-Kompression. Die Bibliothek ist weit verbreitet, da sie klein, effizient und vielseitig ist.
http://de.wikipedia.org/wiki/Gzip

Dennoch weiß ich nicht, welches besser ist. Ich gehe einfach mal von aus, das gzip dem deflate vorzuziehen ist...

Und vor allem, kann ich GzipFile() so benutzen, wie ich es mit
print zlib.compress( HTMLdata ) gemacht hab???

Meine bisherige Variante:

Code: Alles auswählen

import os, sys

def putHTML( HTMLdata ):
    print "Content-Type: text/html"
    modes = os.environ['HTTP_ACCEPT_ENCODING'].split(',')

    if "gzip" in modes:
        from gzip import GzipFile
        
        print 'Content-Encoding: gzip\n'
        oldstdout = sys.stdout
        sys.stdout = GzipFile(mode='wb', fileobj=sys.stdout)
        print HTMLdata
        sys.stdout = oldstdout
        return
    elif "deflate" in modes:
        from zlib import compress
        
        print "Content-Encoding: deflate\n"
        print compress( HTMLdata )
        return
    else:
        print "\n", HTMLdata
Zuletzt geändert von jens am Mittwoch 16. Februar 2005, 11:11, insgesamt 1-mal geändert.
BlackJack

Dienstag 8. Februar 2005, 00:18

jens hat geschrieben:Jep, mit "deflate" klappt es... Jetzt bin ich allerdings ein wenig verwirrt... Worin besteht der Unterschied zwischen gzip und deflate ??? Welches ist besser?

Hab das gefunden:
gzip basiert auf dem deflate-Algorithmus, der eine Kombination aus LZ77 und Huffman-Kodierung ist. deflate wurde als Reaktion auf die Patente entwickelt, die auf LZW und andere Kompressionsalgorithmen bestanden.

Um die Entwicklung von Software zu vereinfachen, die Datenkompression nutzt, wurde die zlib-Bibliothek geschrieben. Sie unterstützt das gzip-Dateiformat und die deflate-Kompression. Die Bibliothek ist weit verbreitet, da sie klein, effizient und vielseitig ist.
Da steht der Unterschied eigentlich. Deflate ist ein Kompressionsalgorithmus und gzip ist ein Dateiformat. Bei der zlib Variante kannst Du Daten als String nehmen, sie komprimieren und bekommst die reinen komprimierten Daten zurück. Bei GzipFile wird in die Datei erst ein Header geschrieben, in dem der Dateiname drinsteht und dann folgen deflate-komprimierte Datenblöcke. Ich glaube eine Prüfsumme wird auch über jeden Block gebildet.

Der Vorteil von GzipFile() beim benutzen in diesem Szenario ist meiner Meinung nach, dass man davon im restlichen Programm nichts merken muss. Man "wrapped" einfach sys.stdout und kann dann ganz normal mit print oder sys.stdout.write() Daten schreiben. Die Daten werden automatisch in Blöcke aufgeteilt, komprimiert und verschickt. Es wird nie mehr Speicher verbraucht, als für diese relativ kleinen Blöcke benötigt wird. Bei zlib.compress() dagegen muss man erst die kompletten Daten lesen und komprimieren.
Benutzeravatar
jens
Moderator
Beiträge: 8461
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Dienstag 8. Februar 2005, 07:03

Muß man dann nicht auch am Ende ein close() machen???

Ich meine, was passiert mit dem letzten, vielleicht gerade erst angefangenen Block? Der wird doch ohne close() garnicht gesendet, oder?

Unabhändig von der Speicherauslastung, die IMHO nur bei super, super langen HTML-Seiten überhaupt erst zum tragen kommt... Ist deflate nicht generell besser, weil kleiner, weil kein Header vorhanden ist??? Eine CRC o.ä. sollte doch überhaupt nicht nötig sein, da bei TCP/IP die Daten doch eigentlich schon Fehlergeschützt sind...
BlackJack

Dienstag 8. Februar 2005, 14:02

Das close() wird am Skriptende implizit gemacht würde ich sagen. Es klappt jedenfalls so. Der Overhead ist nicht gross, bloss ein paar Byte.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dienstag 8. Februar 2005, 14:03

BlackJack hat geschrieben:Das close() wird am Skriptende implizit gemacht würde ich sagen.
Trotzdem sollte man es schon vorher zumachen, das ist guter Stil.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Mittwoch 9. Februar 2005, 21:56

Okay, dann kann man das Modul atexit importieren und nach der Zeile, die sys.stdout durch das GzipFile ersetzt, noch folgendes einfügen:

Code: Alles auswählen

atexit.register(sys.stdout.close)
Benutzeravatar
jens
Moderator
Beiträge: 8461
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Donnerstag 3. März 2005, 18:08

Langsam finde ich gefallen daran stdout zu verbiegen... Nun frage ich mich, ob es auch bei "deflate" mit zlib geht... Ich nehme mal an, dazu muß ich zlib.compressobj() verwenden, oder?
Antworten