Konvertierungsfehler

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Hallo zusammen,

ich habe ein kleines Prbolem mit Umlauten und Sonderzeichen:

Beispiel: stören könnte

Umfeld und Vorgehen:

Ich generiere mit Genshi ein HTML Dokument zusammen (hier stimmts noch)
Anschließend versende ich über httplib.HTTPConnection das ganze an ein Java Servlet (HTTP Post).
Das Javaservlet sendet das ganze in (noch) unveränderter form mittels einen Post-Response
als Content wieder zurück.

Anschließend gebe ich das ganze an den Benutzer zurück und zeige es im Browser an (Umweg saveAs)

Code: Alles auswählen

if fileName and fileExtension:
            req.send_header('Content-Disposition', 'filename=%s.%s' % 
                        (fileName, fileExtension))
        req.end_headers()
        req.write(fileContent)
Aber ok, wahrscheinlich ist es eine einfach Umwandlung, welche vor dem Versenden machen muss?
Ich habe es mit

Code: Alles auswählen

 #file ist ein Stream Object
file = str(file).encode('utf-8')
versucht, wahrscheinlich bring ich auch bei den Formaten etwas durcheinander.

Wie könnte ich das eventuell lösen?

Danke schonmal

Grüße

Stefan
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Code: Alles auswählen

 #file ist ein Stream Object
file = str(file).encode('utf-8')
`str` gibt einen Byte-String zurueck, und `encode` kodiert in einen Byte-String, darum ist `encode` v.a. bei `unicode` Objekten nuetzlich, so ist der Inhalt doppelt kodiert.
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Hallo,

danke für die Antworten.

Das versenden mittels Post funktioniert jetzt ganz gut.
Allerdings habe ich jetzt ein ähnliches Problem

Ich sende von Java ein ODF-File als Post-Response im HTTP.Body zurück an Python (Datenquelle ByteArrayStream)
Die Daten kommen auch vollständig an, allerdings wenn ich die daten mittels Get - Response an den Benutzer zurück
sende wird das ODF File ungültig (auf der Java seite ist es gültig da ich es extra zwischgengespeichert habe, um dort
den Fehler auszuschließen)

An sich die Zwei Methoden

Der Post (nur zu vollständigkeit halber)

Code: Alles auswählen

def httpPost(self,server,port,address,file,template,arguments):

            headers = {"Content-type": "text/html",
                        "Accept": "text/html",
                        "Template":str(template),
                        "Arguments":str(arguments)}

            conn = httplib.HTTPConnection(str(server),str(port),timeout=10000)

            conn.request("POST", str(address), str(file),headers)
            
            responseStatus ="500"
            responseReason = "Internal Server Error"
            try:
                response = conn.getresponse()
                responseStatus = response.status
                responseReason = response.reason
                data = response.read()
            except Exception, e:
                print e
            conn.close()
            return data,responseStatus,responseReason


Get-Versenden (File an den Benutzer senden)

Code: Alles auswählen

def sendOdfFile(self,fileName,fileExtension, fileContent,req,responseStatus,responseReason):
        print type(fileContent)
#        if isinstance(fileContent, unicode): #unicode
#                fileContent = fileContent.encode('utf8')
        print fileContent
        req.send_response("%s %s"%(responseStatus,responseReason))
        req.send_header('Content-Type', '"Content-type": "application/x-www-form-urlencoded"')
        req.send_header('Content-Length', len(fileContent))
        if fileName and fileExtension:
            req.send_header('Content-Disposition', 'filename=%s.%s' % 
                        (fileName, fileExtension))
        req.end_headers()
        req.write(fileContent)
        raise RequestDone
Ausgabe des Files in der Console "sieht eigentlich korrekt aus"
Bild

Ich denke das Problem ist, dass das Ergebnis des Post-Response ein String ist (type(fileContent)) = str,
wahrscheinlich müsste ich das dort in bytes umwandeln?

Oder mach ich einfach nur einen Fehler bei den HTTP Headers?

Danke schonmal

Grüße

Stefan
BlackJack

@rads: Wie stellst Du das "ungültig" denn fest? Ist da irgendwo Windows involviert und Du hast vielleicht vergessen bei `open()` anzugeben, dass es sich um eine Binärdatei handelt?
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Hi und schonmal danke für die Antwort

Ich sende das File ja mit dem Get Request mit und bekomme dann im Browser ein Dialog "Speichern unter"
Anschließend speichere ich das z.b. unter C: ab.
Die gespeicherte Dateigröße stimmt aufs byte mit der ursprungsgröße überein, allerdings meldet Openoffice
das dieses File ungültig ist und repariert werden muss, was natürlich fehl schlägt. Komplett kaputt ist
das file auch nicht, ich kann das file als zip öffnen, auch sind dort nur style.xml und content.xml ungültig,
soweit ich das gesehen habe. Aber wie gesagt, das file ist gültig auf der Serverseite erstellt.

Auf der Javaecke bekomme ich ein HTML File zugesendet (welches ich mit openoffice dann zu einem odf umwandelt)
Hier muss ich auch schon mit

Code: Alles auswählen

String contentConverted = new String(content.toString().getBytes(), UTF_8);
wandeln

Habe das ganze auch im Pyton versucht, allerdins ohne erfolg.
rads hat geschrieben:# if isinstance(fileContent, unicode): #unicode
# fileContent = fileContent.encode('utf8')
Ich wollte ein paar content-types durchprobieren, vielleicht liegt es daran, obwohl ich dachte das es sich
hierbei nur um eine notiz handelt.

Grüße
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Nur noch zum Beweis:

ich speichere testweise die gleichen Daten beim server
Datenquelle: odfContent (ByteArrayOutputStream)
Versende das über einen PrintWriter (unterstützt nur String) mit response.getWriter().println(odfContent)

CodeAuszug

Code: Alles auswählen

ByteArrayOutputStream odfContent;

			odfContent = convertContent(currentFilePath, getTemplateFolder(
					currentFilePath, template), getSoureFolder(currentFilePath,
					session.getId()), request.getHeader(ARGUMENTS));
			// Response
PrintWriter out = response.getWriter();
			
                        #Test zum speichern der Datei #####
			FileOutputStream file = new FileOutputStream(new File("c:/testistest.odf"));
			file.write(odfContent.toByteArray());
			file.flush();
			file.close();
                        #########################			
			
			response.setStatus(response.SC_OK);
out.println(odfContent);
			out.close();

Kann bei der seite maximal am PrintWriter und der Methode println liegen, oder ich brauche hier zwangsläufig einen Content-Type im html header?

Print methode kann es nach Api eigentlich nicht sein:

Code: Alles auswählen

public void print(Object obj)
Prints an object. The string produced by the String.valueOf(Object) method is translated into bytes according to the platform's default character encoding, and these bytes are written in exactly the manner of the write(int) method. 
Hab auch out.write(odfContent.toString()); ausprobiert, aber wie api schon vermuten lässt, keine Änderung.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

rads hat geschrieben: Ich sende das File ja mit dem Get Request mit und bekomme dann im Browser ein Dialog "Speichern unter"
Anschließend speichere ich das z.b. unter C: ab.
Die gespeicherte Dateigröße stimmt aufs byte mit der ursprungsgröße überein, allerdings meldet Openoffice
das dieses File ungültig ist und repariert werden muss, was natürlich fehl schlägt.
Interessant wäre hier zusätzlich, wie es mit dem md5-Wert aussieht! Sind die Dateien exakt gleich, so ist bereits vorher etwas dummes passiert. Wenn Du tatsächlich nur Bytes überträgst, dann sollte da eigentlich nichts schief laufen.

Imho deutet da eh schon viel darauf hin, wenn man die Datei anstandslos als Zip-File entpacken kann...

Also: Wo wir die Datei "manipuliert"? Da muss ja irgend etwas vor dem Senden passieren. Sollte die Datei unmittelbar vor dem Senden (und nach der Manipulation) identisch sein mit der Datei, die schließlich übertragen wurde, dann muss der Fehler zuvor passieren.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Der Fehler liegt definitiv zwischen dem HTTP Post Response auf Java-Seite und deren Verarbeitung auf Python Seite.

Wie gezeigt, ich speichere
- auf Java seite unmittelbar vor dem senden das File lokal ab, und dort ist es gültig

... versenden ...
- auf der Python seite erhalte ich mittels response.read() die gleiche Anzahl der Bytes. Diese speichere ich zu
testzwecken wie folgt ab (also auch wieder direkt unmittelbar)

Code: Alles auswählen

          
                fobj = open("c:/test.odt", "wb") 
                fobj.write(data)
                fobj.close()
als ergebniss von data = response.read() hat zur Folge das data ein 'str' ist (wie oben gezeigt)

Muss quasi ein Fehler beim http post response sein, hat der content-data attributswert (z.b. application/zip)
http://de.selfhtml.org/navigation/suche ... =mime+type

Wie gesagt, die Javaseite versendet einen Bytestring und ist sonst absolut fehlerfrei, da sonst der test mit dem speichern der datei vor dem submit nicht funktionieren würde.


Grüße
Zuletzt geändert von rads am Donnerstag 8. Juli 2010, 15:02, insgesamt 2-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Was ist denn nun mit dem md5-Wert? Gleiche den doch einmal ab!

Ist zip denn wirklich so tollerant? Ich kann mir nicht vorstellen, dass das Archiv noch gültig wäre, wenn durch den Transport Bytes geändert würden... aber auch da hilft md5 ;-)

Was passiert bei anderen Dokumenten? Hast Du es mal mit einer anderen Office-Datei probiert?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Nochmal zum Workflow:
Python erstellt eine html datei (W3C gültig) und sendet es an ein Java Servlet. HTTP Post Request

Das Java Servlet verarbeitet dies mit OpenOffice und gibt ein ODF Dokument über den HTTP Post Response zurück.
>>Fehler<<
Python (server) gibt file an client weiter (HTTP Get Request/Response, noch uninteressant, da davor der fehler)

Ich werd das mit md5 passieren, direkt aus den Bytestream ist ja wohl nicht?
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Bild

Bild

Anbei die gewünschten md5 checksum.
Und bisl screenshots.

Aber das was nicht stimmt wissen wir ja, interessanter wäre ja woran es liegt :)
Die Dateiinformationen weichen ab, da ich auf der javaseite eine odf datei kopiere (und somit als vorlage verwende)
und in dieser dann den kontent einfüge. Nach dem Versenden und speichern des pyhton str in eine zwangsläufig neu
datei hat wohl einen verlust dieser informatinen zur folge, vielleicht wird auch so die datei ungültig?

Na schaun wir mal und suchen weiter.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Noch mal zur Sicherheit:

Kann man das ODF-Dokument nach Erstellung durch das Servlet mit OO.org öffnen?
Kann man das Dokument nach der Übertragung an den Python-Server unzippen ohne Fehlermeldung?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Java- Serverseite: Datei speichern, OOo öffnen möglich und Unzip möglich
Python- Serverseite: OOo nicht möglich Unzip nicht möglich (öffnen/browsen mit Zipprogramm 7zip schon möglich)

Bild

Als Mime Type verwende ich den gleichen wie im OOo dokument standardmässig angegeben: application/vnd.oasis.opendocument.text
Habe hier aber auch schon viele andere verwendet.

Anmerkung: Die Meldung zum Kompressionsverfahren. die xml datei ist im original sowie immer leer, wahrscheinlich deswegen die Meldung.
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

So, keine Ahnung ob das schön ist

aber ich codiere jetzt auf der Javaseite mittels BASE64Encoder()
und auf Python Seite mit base64.b64decode.

Funktioniert wunderbar, hat das für mich irgendwelche Risiken?
(Geschwindigkeit/Datenvolumen ist vernachlässigbar, da könnte man bei dem Reportgenerartor
eher ansetzten)

Grüße Stefan
BlackJack

@rads: Bei dem Code hier:

Code: Alles auswählen

String contentConverted = new String(content.toString().getBytes(), UTF_8);
Was hat `content` für einen Typ? Und ist Dir klar das `getBytes()` ohne Argument problematisch ist, weil es vom System abhängt welche Kodierung da verwendet wird. Wenn das nicht mit der tatsächlichen Kodierung der Daten übereinstimmt, gibt's an der Stelle Probleme.

Ich denke Dein Problem hat was damit zu tun, dass Du versuchst Binärdaten auf der Java-Seite als `String`\s zu behandeln. Das geht nicht. `String` in Java entspricht `unicode` in Python. Darin haben Binärdaten nichts zu suchen. Das gleiche gilt für den `PrintWriter`. Wenn der nur `String` versenden kann, muss man die Binärdaten als Zeichenkette kodieren. Base64 ist eine Möglichkeit dafür.
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

BlackJack hat geschrieben:Was hat `content` für einen Typ?

Code: Alles auswählen

// request ^= HttpServletRequest
//getReader liefert einen BufferedReader
BufferedReader reader = request.getReader();
//Stringbuilder zum zeilenweise einlesen des Inhaltes
StringBuilder content = new StringBuilder();
BlackJack hat geschrieben:Ich denke Dein Problem hat was damit zu tun,
BlackJack hat geschrieben:Dir klar das `getBytes()` ohne Argument problematisch ist,
Nein genau das war mir nicht klar, hatte zwar schon einige verteilte Anwendungen
auf unterschiedlichen Systemem/Betreibssystemen/Architekturen und bis jetzt
kein Problem. Aber danke für den Hinweis, wenn das offenbar zu Fehlern führen kann
dann wird es vielleicht daran liegen.

Vielen Dank Blackjack, wie immer ein Beereicherung für mich :)
BlackJack hat geschrieben:Base64 ist eine Möglichkeit dafür.
Mach ich, nicht ideal aber immerhin eine funktionierende und hoffetlich robuste Lösung.
Antworten