2013-05-26 79 views
7

爲了學習的目的,我使用Python + Flask創建了一個站點。我想從數據庫恢復圖像並在屏幕上顯示它。但一次只能一步。如何將圖像文件保存在Postgres數據庫上?

我不知道如何將圖像保存在我的數據庫中的第一位。我的搜索只顯示我必須在我的數據庫中使用bytea類型。然後,我得到我的圖像,並以某種方式(??)將其轉換爲一個字節數組(bytea ==叮咬數組?),並以某種方式(??)在插入命令中使用此數組。

我能夠發現(也許)如何在Java(here)和C#(here)中做到這一點,但我真的很想使用Python,至少現在。

有人可以幫助我嗎?

這個網站有很多這樣的問題。但大多數(很容易超過85%)的答覆是「你不應該將圖像保存到你的數據庫中,它們屬於fs」並且不能回答這個問題。其餘的並不完全解決我的問題。所以,如果重複有這種答案,請不要將其標記爲重複。

+0

除了bytea,還有使用「大對象」的選項。 [這裏是與手冊鏈接的選項列表。](http://stackoverflow.com/questions/7434530/storing-long-binary-raw-data-strings/7439642#7439642)儘管沒有Python特定的解決方案。 –

+0

好吧,沒有特定的Python。所以一般來說,我與圖像有什麼關係?獲取文件並將其轉換爲字符串?獲取字符串並將其設置爲二進制?我不明白的是在你的fs中的「image.jpg」和它的bytea數據之間會發生什麼。 –

回答

20

我一般不寫的人完整的示例程序,但你沒有要求它,這是一個非常簡單的,所以在這裏你去:

#!/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) 

用Python 3.3編寫。使用Python 2.7需要讀取文件並將其轉換爲buffer對象或使用大對象函數。轉換到Python 2.6及更早版本需要安裝argparse,可能還需要其他更改。

如果要測試運行它,您需要將數據庫連接字符串更改爲適合您的系統的內容。

如果你使用大圖像時可以考慮使用psycopg2's large object support代替bytea - 尤其lo_import店面,lo_export直接寫入文件,大對象讀取功能在同一時間讀取圖像的小塊。

+0

太棒了!這正是我所期待的!我已經設法在之前的答案之後在數據庫中插入一個文件,但是在嘗試恢復時仍然丟失了。當我使用Python 2.7時,我將不得不使用緩衝區對象,就像你說的那樣,但它們看起來非常複雜以至於無法使用!我會做一些研究。謝謝,這真的很有幫助! –

+0

@FelipeMatos ...或者你可以更新到Python3 ;-) –

+0

@FelipeMatos另外,請記住,雖然上面的示例將從數據庫加載的圖像保存到文件中,但您可以輕鬆地將它從緩衝區加載到一個用於顯示的PIL圖像,將它發送給http客戶端等。你很少需要將它寫入磁盤 - 如果你這樣做,你通常會使用'tempfile.TemporaryFile'。 –

0

您可以使用Python的base64編碼和解碼任意二進制字符串到文本字符串中。

+0

你可以,但這真的不是正確的答案。數據庫有效地將二進制數據存儲在'bytea'字段中,因此base64編碼是完全沒有必要的。 –

3

我希望這會對你有用。

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

好吧,我試過這個* *幾乎*工作。爲了將來的參考,我首先從[here](http://www.lfd.uci.edu/~gohlke/pythonlibs/)安裝了Python Image Library。我能夠用你的代碼運行查詢(這是一個很好的信號),但我的數據庫是UFT-8,所以我發現編碼問題。在對編碼進行了一點研究之後,我發現(驚喜!)psycopg支持這種操作,正確[here](http://initd.org/psycopg/docs/usage.html#adapt-binary)。我設法在這些步驟之後插入條目,現在我必須找出如何恢復它。 –

+1

不需要在PIL中實際加載圖像,只需要讀取文件並將其存儲在數據庫中。儘管如此,+1是一個有用的例子。 –

+0

@CraigRinger你是對的。但是,如果圖像被修改並存儲爲縮略圖。我想這會很有用。 :) – 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

當我嘗試這個時,我得到'*** TypeError:無法將實例轉換爲二進制' – user208859

+0

將數據轉換爲'psycopg2.Binary'是我的關鍵,謝謝! – Matt

0

這是我的解決方案,它可以在我的網站工作:

@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) 

使用瓶,瓶,鍊金來處理數據庫。

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

這就是html文件,你可以將它嵌入到你的html中。

相關問題