2013-05-26 15 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

좋아, 아니 파이썬 특정. 그래서 일반적으로 말하면 이미지와 무엇을해야합니까? 파일을 가져 와서 문자열로 변환 하시겠습니까?문자열을 가져 와서 바이너리로 만드시겠습니까? 내가 이해하지 못하는 것은 당신의 fs에서 "image.jpg"와 그것의 바이트 데이터를 갖는 것 사이에서 일어나는 일이다. –

답변

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) 

파이썬 3.3으로 작성되었습니다. Python 2.7을 사용하려면 파일을 읽고 buffer 객체로 변환하거나 대형 객체 함수를 사용해야합니다. Python 2.6 및 그 이전 버전으로 변환하려면 argparse를 설치해야합니다. 아마도 다른 변경 사항이 필요합니다.

테스트를 실행할 경우 데이터베이스 연결 문자열을 시스템에 적합한 것으로 변경해야합니다.

당신이 psycopg2's large object support 대신 bytea 사용을 고려 큰 이미지로 작업하는 경우 - 파일에 직접 쓰기 위해, lo_export 저장소에 대한 lo_import, 특히, 그리고 대형 개체가 한 번에 이미지의 작은 덩어리를 읽는 기능을 읽어 .

내 솔루션입니다
+0

좋아요! 그게 정확히 내가 찾고 있던거야! 나는 이미 이전 답변에 따라 데이터베이스에 파일을 삽입 할 수 있었지만 복구를 시도 할 때 여전히 손실되었습니다. 파이썬 2.7을 사용할 때 버퍼 오브젝트를 사용해야 할 것입니다.하지만 그렇게 사용하기에는 복잡해 보입니다! 나는 그것에 대한 연구를 할 것이다. 고마워, 이건 정말 도움이되었다! –

+0

@FelipeMatos ... 또는 Python3으로 업데이트 할 수 있습니다 .-) –

+0

@FelipeMatos 또한 위 예제에서는 데이터베이스에서로드 된 이미지를 파일에 저장하지만 버퍼에서 파일로 쉽게로드 할 수 있습니다 디스플레이를위한 PIL 이미지, http 클라이언트에 보내는 등등. 당신은 거의 그것을 디스크에 쓸 필요가 거의 없으며, 그렇게한다면 일반적으로'tempfile.TemporaryFile'을 사용합니다. –

0

임의의 2 진 문자열을 텍스트 문자열로 인코딩 및 디코딩하기 위해 Python의 base64을 사용할 수 있습니다.

+0

할 수는 있겠지만 실제로는 올바른 대답이 아닙니다. 데이터베이스는 2 진 데이터를'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

좋아, 나는 이것을 시도하고 거의 * 일했다. 나중에 참조 할 수 있도록 필자는 Python Image Library를 [here] (http://www.lfd.uci.edu/~gohlke/pythonlibs/)에서 처음 설치했습니다. 귀하의 코드 (훌륭한 신호)를 사용하여 쿼리를 실행할 수 있었지만 데이터베이스가 UFT-8이므로 인코딩 문제가 발견되었습니다. Encode에 관한 약간의 조사가 끝난 후, psycopg가 이러한 종류의 작업을 지원한다는 것을 발견했습니다. (여기에 놀랍습니다!) (http://initd.org/psycopg/docs/usage.html#adapt-binary). 나는 그 단계들을 따라 엔트리를 삽입 할 수있게되었는데, 이제 그것을 복구하는 방법을 찾아야 만한다. –

+1

실제로 이미지를 PIL로로드 할 필요는 없습니다. 파일을 읽고 DB에 저장하기 만하면됩니다. 그럼에도 불구하고 +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에 그것을 포함시킬 수 있습니다.

관련 문제