2013-05-26 13 views
7

Für Lernzwecke erstelle ich eine Website mit Python + Flask. Ich möchte ein Bild aus der Datenbank wiederherstellen und auf dem Bildschirm anzeigen. Aber ein Schritt nach dem anderen.Wie speichere ich eine Bilddatei in einer Postgres-Datenbank?

Ich habe keine Ahnung, wie ein Bild in meiner Datenbank in erster Linie gespeichert wird. Meine Suche ergab nur, dass ich einen bytea Typ in meiner Datenbank verwenden muss. Dann bekomme ich mein Bild und irgendwie (??) wandle es in ein Array von Bytes um (bytea == Array von Bissen?) Und irgendwie (??) benutze dieses Array in einem Insert-Befehl.

Ich konnte (vielleicht) herausfinden, wie man es in Java (here) und C# (here) macht, aber ich würde wirklich gerne Python verwenden, zumindest für jetzt.

Kann mir jemand helfen?

Es gibt Tonnen von Fragen dieser Art auf dieser Seite. Aber die meisten (leicht über 85%) von ihnen werden geantwortet als "Sie sollten Bilder in Ihrer Datenbank nicht speichern, sie gehören in fs" und beantworten die Frage nicht. Der Rest löst mein Problem nicht ganz. Bitte markieren Sie das nicht als Duplikat, wenn das Duplikat diese Art von Antwort enthält.

+0

Neben Bytea gibt es auch die Option "große Objekte" zu verwenden. [Hier ist eine Liste von Optionen mit Links zum Handbuch.] (Http://stackoverflow.com/questions/7434530/storing-long-binary-raw-data-strings/7439642#7439642) Keine Python-spezifische Lösung. –

+0

Ok, kein Python-spezifisch. Was muss ich allgemein mit dem Bild anfangen? Holen Sie sich die Datei und transformieren Sie sie in eine Zeichenfolge?Holen Sie die Zeichenfolge und machen Sie es binär? Was ich nicht verstehe, ist, was zwischen "image.jpg" in Ihrem fs passiert und die bytea Daten davon hat. –

Antwort

20

Ich schreibe normalerweise nicht vollständige Beispielprogramme für die Menschen, aber du hast verlangen es nicht und es ist ein ziemlich einfaches, so hier geht:

#!/usr/bin/env python3 

import os 
import sys 
import psycopg2 
import argparse 

db_conn_str = "dbname=regress user=craig" 

create_table_stm = """ 
CREATE TABLE files (
    id serial primary key, 
    orig_filename text not null, 
    file_data bytea not null 
) 
""" 

def main(argv): 
    parser = argparse.ArgumentParser() 
    parser_action = parser.add_mutually_exclusive_group(required=True) 
    parser_action.add_argument("--store", action='store_const', const=True, help="Load an image from the named file and save it in the DB") 
    parser_action.add_argument("--fetch", type=int, help="Fetch an image from the DB and store it in the named file, overwriting it if it exists. Takes the database file identifier as an argument.", metavar='42') 
    parser.add_argument("filename", help="Name of file to write to/fetch from") 

    args = parser.parse_args(argv[1:]) 

    conn = psycopg2.connect(db_conn_str) 
    curs = conn.cursor() 

    # Ensure DB structure is present 
    curs.execute("SELECT 1 FROM information_schema.tables WHERE table_schema = %s AND table_name = %s", ('public','files')) 
    result = curs.fetchall() 
    if len(result) == 0: 
     curs.execute(create_table_stm) 

    # and run the command 
    if args.store: 
     # Reads the whole file into memory. If you want to avoid that, 
     # use large object storage instead of bytea; see the psycopg2 
     # and postgresql documentation. 
     f = open(args.filename,'rb') 

     # The following code works as-is in Python 3. 
     # 
     # In Python 2, you can't just pass a 'str' directly, as psycopg2 
     # will think it's an encoded text string, not raw bytes. You must 
     # either use psycopg2.Binary to wrap it, or load the data into a 
     # "bytearray" object. 
     # 
     # so either: 
     # 
     # filedata = psycopg2.Binary(f.read()) 
     # 
     # or 
     # 
     # filedata = buffer(f.read()) 
     # 
     filedata = f.read() 
     curs.execute("INSERT INTO files(id, orig_filename, file_data) VALUES (DEFAULT,%s,%s) RETURNING id", (args.filename, filedata)) 
     returned_id = curs.fetchone()[0] 
     f.close() 
     conn.commit() 
     print("Stored {0} into DB record {1}".format(args.filename, returned_id)) 

    elif args.fetch is not None: 
     # Fetches the file from the DB into memory then writes it out. 
     # Same as for store, to avoid that use a large object. 
     f = open(args.filename,'wb') 
     curs.execute("SELECT file_data, orig_filename FROM files WHERE id = %s", (int(args.fetch),)) 
     (file_data, orig_filename) = curs.fetchone() 

      # In Python 3 this code works as-is. 
      # In Python 2, you must get the str from the returned buffer object. 
     f.write(file_data) 
     f.close() 
     print("Fetched {0} into file {1}; original filename was {2}".format(args.fetch, args.filename, orig_filename)) 

    conn.close() 

if __name__ == '__main__': 
    main(sys.argv) 

Mit Python 3.3 geschrieben. Wenn Sie Python 2.7 verwenden, müssen Sie die Datei lesen und in ein buffer-Objekt konvertieren oder die Funktionen für große Objekte verwenden. Die Konvertierung in Python 2.6 und älter erfordert die Installation von argparse, wahrscheinlich andere Änderungen.

Sie sollten die Datenbankverbindungszeichenfolge in etwas ändern, das für Ihr System geeignet ist, wenn Sie es testen möchten.

Wenn Sie mit großen Bildern arbeiten prüfen, mit psycopg2's large object support statt bytea - insbesondere lo_import für Geschäft, lo_export für direkt in eine Datei zu schreiben, und das große Objekt Lesefunktionen für das Lese kleine Stücke des Bildes zu einer Zeit, .

+0

Großartig! Genau das habe ich gesucht! Ich hatte es bereits geschafft, eine Datei nach einer vorherigen Antwort in die Datenbank einzufügen, aber ich war immer noch verloren, als ich versuchte, sie wiederherzustellen. Da ich Python 2.7 verwende, muss ich ein Pufferobjekt verwenden, wie Sie sagten, aber sie sehen so komplex aus zu verwenden! Ich werde etwas darüber nachforschen. Danke, das war wirklich hilfreich! –

+0

@FelipeMatos ... oder Sie könnten auf Python3 aktualisieren ;-) –

+0

@FelipeMatos Denken Sie daran, dass das obige Beispiel das von der Datenbank geladene Bild in eine Datei speichert, Sie es aber genauso einfach aus dem Puffer laden können ein PIL-Bild für die Anzeige, senden Sie es an einen http-Client, etc. Sie müssen sehr selten auf die Festplatte schreiben - und wenn Sie dies tun, würden Sie in der Regel 'tempfile.TemporaryFile' verwenden. –

0

Sie können Pythons base64 zum Kodieren und Dekodieren beliebiger Binärzeichenfolgen in Textzeichenfolgen verwenden.

+0

Sie können, aber das ist wirklich nicht die richtige Antwort. Die Datenbank speichert binäre Daten effizient in 'bytea' Feldern, so dass eine base64-Codierung völlig unnötig ist. –

3

Ich hoffe, dass dies für Sie arbeiten wird.

import Image 
import StringIO 
im = Image.open("file_name.jpg") # Getting the Image 
fp = StringIO.StringIO() 
im.save(fp,"JPEG") 
output = fp.getvalue() # The output is 8-bit String. 

StringIO Image

+0

Ok, ich habe es versucht und * fast * funktioniert. Für zukünftige Referenz habe ich zuerst Python Image Library von [hier] (http://www.lfd.uci.edu/~gohlke/pythonlibs/) installiert. Ich konnte eine Abfrage mit Ihrem Code ausführen (was ein großartiges Signal ist), aber meine Datenbank ist UFT-8, also habe ich Probleme mit der Codierung gefunden. Nach ein bisschen Forschung, die auf Kodierung gerichtet ist, entdeckte ich, dass (Überraschung!) Psycopg diese Art von Operation unterstützt, richtig [hier] (http://initd.org/psycopg/docs/usage.html#adapt-binary). Ich schaffe es, den Eintrag nach diesen Schritten einzufügen, jetzt muss ich herausfinden, wie ich ihn wiederherstellen kann. –

+1

Es ist nicht notwendig, das Bild in PIL zu laden, Sie müssen nur die Datei lesen und in der Datenbank speichern. Trotzdem +1 für ein nützliches Beispiel. –

+0

@CraigRinger Sie haben Recht. Aber wenn das Bild verändert und als Thumbnail gespeichert wird. Ich denke, das wird nützlich sein. :) – iraycd

3
import psycopg2 
import sys 

def readImage(): 
    try: 
     fin = open("woman.jpg", "rb") 
     img = fin.read() 
     return img 

    except IOError, e: 
     print "Error %d: %s" % (e.args[0],e.args[1]) 
     sys.exit(1) 

    finally: 
     if fin: 
      fin.close() 
try: 
    con = psycopg2.connect(database="testdb", user="abc") 
    cur = con.cursor() 
    data = readImage() 
    binary = psycopg2.Binary(data) 
    cur.execute("INSERT INTO images(id, data) VALUES (1, %s)", (binary,)) 
    con.commit() 
except psycopg2.DatabaseError, e: 
    if con: 
     con.rollback() 
    print 'Error %s' % e  
    sys.exit(1) 
finally: 
    if con: 
     con.close() 
+0

Wenn ich das versuche, bekomme ich '*** TypeError: kann nicht zu binary-Instanz entkommen. – user208859

+0

das Übertragen der Daten zu' psycopg2.Binary' war der Schlüssel für mich, danke! – Matt

0

, dass meine Lösung ist, kann es in meiner Website arbeiten:

@main.route('/upload', methods=['GET', 'POST']) 
def upload_avatar(): 
    if request.method == 'POST': 
     file = request.files['file'] 
     if file and allowed_file(file.filename): 
      current_user.avatar_local = file.read() 
      db.session.add(current_user) 
      db.session.commit() 
      return redirect(url_for('main.user_page', username=current_user.username)) 
    return render_template('upload_avatar.html', user=current_user) 

mit Glaskolben, Flask-Alchemy-Datenbank zu handhaben.

{% block edit_avatar %} 
    <form action="" method=post enctype=multipart/form-data> 
     <p><input type=file name=file> 
     <input type=submit value=Upload> 
    </form> 
{% endblock %} 

das ist HTML-Datei.Sie können es in Ihrem HTML einbetten.

Verwandte Themen