Seite 1 von 2

PDF als bytea in Postgres-DB einfügen

Verfasst: Mittwoch 16. Februar 2011, 10:37
von andi24
Hallo zusammen,

ich möchte gerne über ein Python-Script eine PDF-Datei in das bytea-Feld einer Tabelle in einer PG-Datenbank schreiben.

Die Tabelle sieht folgendermaßen aus:

Code: Alles auswählen

-- Table: test

-- DROP TABLE test;

CREATE TABLE test
(
  id serial NOT NULL,
  "document" bytea,
  "text" text
)
WITH (
  OIDS=FALSE
);
ALTER TABLE test OWNER TO um;
COMMENT ON TABLE test IS 'Testtabelle';

Mein Problem ist, dass ich nicht weiß, was für das bytea-Feld als Eingabe erwartet wird. Mein Versuch sieht so aus:

Code: Alles auswählen

    dateipfad = "/home/user/meinedatei.pdf"
    text = "bla bla"

    sql="""INSERT INTO test (document, text) VALUES (%s, '%s')""" % (dateipfad, text)
    
    curs.execute(sql)
    conn.commit()
        
Fehlermeldung:

Code: Alles auswählen

psycopg2.ProgrammingError: syntax error at or near "/"
LINE 1: INSERT INTO test (document, text) VALUES (/home/user/...

Kann mir da jemand weiterhelfen?

Vielen Dank für jeden Tip!!

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Mittwoch 16. Februar 2011, 12:08
von sparrow
Versuch mal:

Code: Alles auswählen

sql = """INSERT INTO test (document, text) VALUES (?, ?)"""
cursor.execute(sql, (dateipfad, psycopg.Binary(text)) )
oder:

Code: Alles auswählen

sql = """INSERT INTO test (document, text) VALUES (?, ?)"""
cursor.execute(sql, (dateipfad, psycopg2.Binary(text)) )
Ich bin mir gerade nicht sicher und bin nicht daheim um zu schauen ;)

Gruß
Sparrow


Edit: zweites Beispiel sollte funktionieren. Das Lesen funktioniert dann ohne Spezialbehandlung.

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Mittwoch 16. Februar 2011, 12:12
von Hyperion
Willst Du das gesamte Dokument in die DB schreiben, oder nur den Pfad? Bei ersterem musst Du die Datei vorher wohl erst einlesen und den Inhalt der Query übergeben, bei letzterem verstehe ich nicht, wieso Du keinen String als Typen wählst.

Das Hauptproblem ist der falsche (und gefährliche!) Aufbau der dynamischen Query, aber das hat Sparrow ja schon beantwortet.

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Freitag 18. Februar 2011, 09:19
von andi24
Hallo ihr zwei,

vielen Dank für euere Antworten!!

Ja, ich möchte die Datei selbst in der DB speichern und nicht den Pfad als String. Ich hab jetzt eueren Rat befolgt, die Datei erst einzulesen (Reicht mir da ein open?), was dann so aussieht:

Code: Alles auswählen

	text = "bla bla"
	sql="""INSERT INTO um.test (document, text) VALUES ('%s'::bytea, '%s')""" % (dateipfad, text)

	# Datei einlesen
	file = open ("/home/user/myfile.pdf","r")

	# DB-Verbindung herstellen
	conn = initDBConnection()
	curs = createCursor(conn)
	 
	# Daten zur DB schicken
	curs.execute(sql, (file, psycopg2.Binary(text)) )
	conn.commit()
		
	# DB-Verbindung schließen
	closeConnection(conn)	
Fehlermeldung bekomme ich jetzt keine mehr und in der Datenbank steht zunächst mal der Pfad als Text drin. Haut das so hin, wie ich es versucht habe?

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Freitag 18. Februar 2011, 09:24
von Hyperion
andi24 hat geschrieben:Hallo ihr zwei,

vielen Dank für euere Antworten!!
Nuja, Du ignorierst die Inhalte ja teilweise ;-)

Das Zusammenbauen Deines Strings ist immer noch genauso wie vorher!
andi24 hat geschrieben: Fehlermeldung bekomme ich jetzt keine mehr und in der Datenbank steht zunächst mal der Pfad als Text drin. Haut das so hin, wie ich es versucht habe?
Nein! Lies doch mal dir Doku zu open() und weiterführend zu file-Objekten. Du musst den Inhalt schon noch einlesen. Außerdem solltest Du das im Binärmodus erledigen:

Code: Alles auswählen

with open(filename, "rb") as handler:
    # hier kannst Du dann die Daten einlesen

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Freitag 18. Februar 2011, 09:56
von andi24
Oh, entschuldige - das kann natürlich nicht funktionieren, wenn ich die Datei nur öffen, sie aber nicht einlese. Man sollte nicht vor dem ersten Kaffee programmieren :-)

Ich probiers gleich nochmal ...

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Freitag 18. Februar 2011, 10:03
von andi24
So, neuer Versuch:

Code: Alles auswählen

	text = "bla bla"

	# Datei einlesen
	with open ("/home/user/meinedatei.pdf","rb") as f:
		filedata = f.read()
	f.closed

	# DB-Verbindung herstellen
	conn = initDBConnection()
	curs = createCursor(conn)
	
	sql="""INSERT INTO test (document, text) VALUES ('%s'::bytea, '%s')""" % (filedata, text)
	 
	#curs.execute(sql)
	curs.execute(sql, (filedata, psycopg2.Binary(text)) )
	conn.commit()
		

	# DB-Verbindung schließen
	closeConnection(conn)	
Jetzt gibts zumindest mal eine Fehlermeldung:

Code: Alles auswählen

    curs.execute(sql, (filedata, psycopg2.Binary(text)) )
IndexError: tuple index out of range
...verstehen tu ich sie nur leider nicht. Was sollte er denn da für ein Index-Problem haben?

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Freitag 18. Februar 2011, 10:16
von Hyperion

Code: Alles auswählen

f.closed
gibt schon mal keinen Sinn - läuft das denn durch? Der Vorteil won "with" ist ja grad, dass man Dateien nicht explizit schließen muss, sondern dass Python sich da selber drum kümmert.

Zum Fehler: Du solltest Dir mal genau angucken, was Sparrow Dir geschrieben hat! Genau! Dann klappt's sicher auch. ;-)

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Freitag 18. Februar 2011, 10:19
von sparrow
Warum verwendest du das SQL-Statement nicht wie von mir vorgeschlagen?
Und warum führst du dort einen expliziten Cast durch? Hat es sonst nicht funktioniert?

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Freitag 18. Februar 2011, 10:32
von andi24
Oh, stimmt, ich hatte das SQL-Statement garnicht übernommen. Hier die neue Version:

Code: Alles auswählen

	text = "bla bla"

	# Datei einlesen
	with open ("/home/user/meinedatei.pdf","rb") as f:
		filedata = f.read()


	# DB-Verbindung herstellen
	conn = initDBConnection()
	curs = createCursor(conn)
	
	sql="""INSERT INTO test (document, text) VALUES (?, ?)"""
	 
	curs.execute(sql, (filedata, psycopg2.Binary(text)) )
	conn.commit()
		

	# DB-Verbindung schließen
	closeConnection(conn)	
Aber wo sollte ich einen expliziten Cast durchführen?

Bekommen tu ich zu diesem Code jetzt komischerweise einen Syntaxfehler ... seltsam:

Code: Alles auswählen

    curs.execute(sql, (filedata, psycopg2.Binary(text)) )
psycopg2.ProgrammingError: syntax error at or near ","
LINE 1: INSERT INTO test (document, text) VALUES (?, ?)
Hab das doch genau gemacht wie von Dir vorgeschlagen? Oder überseh ich da was?

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Freitag 18. Februar 2011, 10:33
von /me
andi24 hat geschrieben:

Code: Alles auswählen

	sql="""INSERT INTO test (document, text) VALUES ('%s'::bytea, '%s')""" % (filedata, text)
Bitte schau dir an, was in diesem Thread bereits geschrieben wurde. Man baut NIEMALS ein SQL-Statement aus Rohdaten so zusammen. Damit reißt du dir Sicherheitslöcher in Scheunentorgröße ins System.

Das passt ja ohnehin nicht zum später erfolgenden execute().

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Freitag 18. Februar 2011, 10:42
von /me
andi24 hat geschrieben:

Code: Alles auswählen

	sql="""INSERT INTO test (document, text) VALUES (?, ?)"""
	 
	curs.execute(sql, (filedata, psycopg2.Binary(text)) )
Unterstützt psycopg2 wirklich die "?"-Syntax? Ein ganz kurzer Blick auf http://packages.python.org/psycopg2/usa ... ql-queries zeigt mir da eine Variante mit %s.

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Freitag 18. Februar 2011, 14:08
von sparrow
Andi24 hat geschrieben:Aber wo sollte ich einen expliziten Cast durchführen?
::bytea ist ein ein Cast. Du machst also aus irgendetwas ein bytea-Objekt. Das hat mich verwundert.
/me hat geschrieben:Unterstützt psycopg2 wirklich die "?"-Syntax? Ein ganz kurzer Blick auf http://packages.python.org/psycopg2/usa ... ql-queries zeigt mir da eine Variante mit %s.
Deshalb hab ich ja gefragt ob die Query nicht funkitoniert.

Code: Alles auswählen

        sql="""INSERT INTO test (document, text) VALUES (%s, %s)"""         
        curs.execute(sql, (filedata, psycopg2.Binary(text)) )
@Andi24: das von /me verlinkte Dokument enthält viele wichtige Informationen. Auch warum man den String nicht selsbt zusammen klebt sondern die Werte als Parameter bei execute mitgibt. Stichwort: SQL-Injection.

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Dienstag 22. Februar 2011, 10:58
von andi24
Hallo zusammen,

entschuldigt bitte die späte Reaktion, aber ich hatte jetzt für ein paar Tage keine Zeit daran weiterzuarbeiten :-)

@/me:
Vielen Dank für den Link - die Infos dort haben mir weitergeholfen. Ich weiß nur nicht, ob ich sie richtig umsetzen konnte :-) Mein neuer Versuch:

Code: Alles auswählen

	dateipfad = "/home/user/mypdf.pdf"
	mytext = "bla bla"

	# Datei einlesen
	with open (dateipfad,"rb") as f:
		filedata = f.read()

	# DB-Verbindung herstellen
	conn = initDBConnection()
	curs = createCursor(conn)
	
	curs.execute(
		"""INSERT INTO um.test (document, text) VALUES (%(bytea)s, %(text)s);""",
		{'bytea':filedata, 'text':mytext})

	conn.commit()
		
	# DB-Verbindung schließen
	closeConnection(conn)	
bringt die Fehlermeldung:

Code: Alles auswählen

psycopg2.DataError: invalid input syntax for type bytea
LINE 1: INSERT INTO um.test (document, text) VALUES (E'%PDF-1.4

Lieg ich denn da mit dem "bytea" falsch? Ich hab das Datenbankfeld jedenfalls als bytea definiert...
Bin langsam echt am Verzweifeln :-(

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Dienstag 22. Februar 2011, 11:42
von sparrow
Öhm... ja.
Du machst es ja auch wieder anders?

Du musst den Inhalt des Binären-Feldes einmal neu encoden. Darum geht es den ganzen Thread. Und dafür gibt es: psycopg2.Binary(value)

Also:

Code: Alles auswählen

        curs.execute(
                """INSERT INTO um.test (document, text) VALUES (%(bytea)s, %(text)s);""",
                {'bytea' : psycopg2.Binary(filedata), 'text' : mytext})

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Mittwoch 23. Februar 2011, 13:21
von andi24
Ah, ok ... das wusste ich nicht. Vielen Dank!! Jetzt scheint es auch zu funktionieren!

Leider hab ich jetzt noch ein Problem mit der Zeile:

Code: Alles auswählen

with open ("/home/user/mypdf.pdf","rb") as f:
da ich mit Python 2.5.2 arbeite und bekomme deshalb folgende Fehlermeldung:

Code: Alles auswählen

Warning: 'with' will become a reserved keyword in Python 2.6
  File "meinprogramm.py", line 71
    with open ("/home/user/meinpdf.pdf","rb") as f:
            ^
SyntaxError: invalid syntax
Da es sich um einen Server im laufenden Betrieb handelt, habe ich gerade kurzfristig auch nicht die Möglichkeit, die Python-Version zu aktuallisieren. Gibts da eine Möglichkeit, die auch unter 2.5.2 lauffähig ist?

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Mittwoch 23. Februar 2011, 13:30
von sparrow

Code: Alles auswählen

from __future__ import with_statement
Damit holst du dir das für 2.6 geplante feature (sagt ja deine Fehlermeldung) auf 2.5

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Mittwoch 23. Februar 2011, 13:40
von andi24
Super, das war genau das, was ich gesucht hatte! Vielen Dank!!!

Jetzt funktioniert auch alles. Vielen Dank nochmal an alle, die mir geholfen haben!

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Mittwoch 23. Februar 2011, 14:08
von andi24
Ah, eine Sache bereitet mir doch noch ein Problem: Die Datei soll als base64 in die Datenbank geschrieben werden. Mein Versuch war:

Code: Alles auswählen

        with open ("/home/user/mypdf.pdf","rb") as f:
                filedata = f.read().decode("base64")
Hat aber leider nicht geklappt. Fehlermeldung gabs keine, aber beim Öffnen der Datei gibs der Acrobat Reader eine Fehlermeldung aus.

Hab ich das so richtig gemacht?

Re: PDF als bytea in Postgres-DB einfügen

Verfasst: Mittwoch 23. Februar 2011, 14:15
von Hyperion
andi24 hat geschrieben: Hat aber leider nicht geklappt. Fehlermeldung gabs keine, aber beim Öffnen der Datei gibs der Acrobat Reader eine Fehlermeldung aus.
Naja, Du musst das ja beim Auslesen ja wohl auch wieder zurückwandeln - der Acrobat Reader kann doch bestimmt kein Base64 codiertes Dokument lesen, oder?