PDF als bytea in Postgres-DB einfügen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
andi24
User
Beiträge: 56
Registriert: Freitag 5. März 2010, 11:42

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!!
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

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.
Zuletzt geändert von sparrow am Mittwoch 16. Februar 2011, 12:28, insgesamt 1-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
andi24
User
Beiträge: 56
Registriert: Freitag 5. März 2010, 11:42

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?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
andi24
User
Beiträge: 56
Registriert: Freitag 5. März 2010, 11:42

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 ...
andi24
User
Beiträge: 56
Registriert: Freitag 5. März 2010, 11:42

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?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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. ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

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?
andi24
User
Beiträge: 56
Registriert: Freitag 5. März 2010, 11:42

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?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

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().
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

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.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

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.
andi24
User
Beiträge: 56
Registriert: Freitag 5. März 2010, 11:42

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 :-(
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Ö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})
andi24
User
Beiträge: 56
Registriert: Freitag 5. März 2010, 11:42

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?
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

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
andi24
User
Beiträge: 56
Registriert: Freitag 5. März 2010, 11:42

Super, das war genau das, was ich gesucht hatte! Vielen Dank!!!

Jetzt funktioniert auch alles. Vielen Dank nochmal an alle, die mir geholfen haben!
andi24
User
Beiträge: 56
Registriert: Freitag 5. März 2010, 11:42

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?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten