2013-05-26 81 views
7

Vì mục đích học tập, tôi đang tạo một trang web bằng cách sử dụng Python + Flask. Tôi muốn khôi phục một hình ảnh từ cơ sở dữ liệu và hiển thị nó trên màn hình. Nhưng một bước tại một thời điểm.Làm thế nào để lưu tệp hình ảnh trên cơ sở dữ liệu Postgres?

Tôi không biết cách lưu hình ảnh trong cơ sở dữ liệu của mình ngay từ đầu. Tìm kiếm của tôi chỉ tiết lộ rằng tôi phải sử dụng loại bytea trong cơ sở dữ liệu của mình. Sau đó, tôi nhận được hình ảnh của tôi và bằng cách nào đó (??) chuyển đổi nó thành một mảng byte (bytea == mảng cắn?) Và bằng cách nào đó (??) sử dụng mảng này trong một lệnh chèn.

Tôi đã có thể khám phá (có thể) cách thực hiện trong Java (here) và C# (here), nhưng tôi thực sự muốn sử dụng Python, ít nhất là bây giờ.

Ai đó có thể giúp tôi không?

Có rất nhiều câu hỏi thuộc loại này trong trang web này. Nhưng hầu hết (dễ dàng hơn 85%) trong số họ được trả lời là "Bạn không nên lưu hình ảnh trong cơ sở dữ liệu của bạn, họ thuộc về fs" và không trả lời được câu hỏi. Phần còn lại không hoàn toàn giải quyết vấn đề của tôi. Vì vậy, vui lòng không đánh dấu mục này là trùng lặp nếu trùng lặp có loại câu trả lời này.

+0

Bên cạnh đó, cũng có tùy chọn sử dụng "đối tượng lớn". [Đây là danh sách các tùy chọn có liên kết đến hướng dẫn.] (Http://stackoverflow.com/questions/7434530/storing-long-binary-raw-data-strings/7439642#7439642) Mặc dù không có giải pháp cụ thể của Python. –

+0

Ok, không có Python cụ thể. Nói chung, tôi phải làm gì với hình ảnh? Lấy tập tin và chuyển đổi nó trong một chuỗi?Lấy chuỗi và làm cho nó nhị phân? Những gì tôi không hiểu là những gì xảy ra giữa "image.jpg" trong fs của bạn và có dữ liệu bytea của nó. –

Trả lời

20

Tôi không thường viết các chương trình ví dụ hoàn chỉnh cho mọi người, nhưng bạn không đòi hỏi điều đó và đó là một trong khá đơn giản, vì vậy ở đây bạn đi:

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

Được viết bằng Python 3.3. Sử dụng Python 2.7 yêu cầu bạn đọc tệp và chuyển đổi thành đối tượng buffer hoặc sử dụng các hàm đối tượng lớn. Chuyển đổi sang Python 2.6 trở lên yêu cầu cài đặt argparse, có thể là các thay đổi khác.

Bạn sẽ muốn thay đổi chuỗi kết nối cơ sở dữ liệu thành thứ gì đó phù hợp với hệ thống của bạn nếu bạn định chạy thử nó.

Nếu bạn đang làm việc với hình ảnh lớn xem xét sử dụng psycopg2's large object support thay vì bytea - nói riêng, lo_import cho cửa hàng, lo_export để viết trực tiếp vào một tập tin, và các đối tượng lớn đọc chức năng cho việc đọc khối nhỏ của hình ảnh tại một thời điểm .

+0

Tuyệt vời! Đó chính xác là những gì tôi đang tìm kiếm! Tôi đã quản lý để chèn một tập tin trong cơ sở dữ liệu sau một câu trả lời trước đó, nhưng tôi vẫn bị mất khi cố gắng phục hồi nó. Khi tôi đang sử dụng Python 2.7, tôi sẽ phải sử dụng một đối tượng đệm, như bạn đã nói, nhưng chúng trông rất phức tạp để sử dụng! Tôi sẽ làm một số nghiên cứu về nó. Cảm ơn, điều này thực sự hữu ích! –

+0

@FelipeMatos ... hoặc bạn có thể cập nhật lên Python3 ;-) –

+0

@FelipeMatos Ngoài ra, hãy nhớ rằng trong khi ví dụ trên lưu hình ảnh được tải từ cơ sở dữ liệu vào tệp, bạn có thể dễ dàng tải nó từ bộ đệm vào một hình ảnh PIL để hiển thị, gửi nó đến một máy khách http, vv Bạn rất hiếm khi cần phải ghi nó vào đĩa - và nếu bạn làm thế, bạn thường sử dụng 'tempfile.TemporaryFile'. –

0

Bạn có thể sử dụng mã số base64 của Python để mã hóa và giải mã chuỗi nhị phân tùy ý thành chuỗi văn bản.

+0

Bạn có thể, nhưng đó thực sự không phải là câu trả lời đúng. Cơ sở dữ liệu lưu trữ hiệu quả dữ liệu nhị phân trong các trường 'bytea', vì vậy mã hóa base64 hoàn toàn không cần thiết. –

3

Tôi hy vọng điều này sẽ phù hợp với bạn.

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, tôi đã thử điều này và * gần như * đã hoạt động. Để tham khảo trong tương lai, lần đầu tiên tôi cài đặt Thư viện hình ảnh Python từ [here] (http://www.lfd.uci.edu/~gohlke/pythonlibs/). Tôi đã có thể chạy một truy vấn với mã của bạn (đó là một tín hiệu tuyệt vời), nhưng cơ sở dữ liệu của tôi là UFT-8, vì vậy tôi thấy vấn đề với mã hóa. Sau một chút nghiên cứu hướng đến mã hóa, tôi phát hiện ra rằng (bất ngờ!) Psycopg hỗ trợ loại hoạt động này, ngay tại đây (http://initd.org/psycopg/docs/usage.html#adapt-binary). Tôi quản lý để chèn các mục nhập sau các bước, bây giờ tôi phải tìm hiểu làm thế nào để phục hồi nó. –

+1

Không cần phải tải hình ảnh trong PIL, bạn chỉ cần đọc tệp và lưu trữ nó trong DB. Tuy nhiên, +1 cho một ví dụ hữu ích. –

+0

@CraigRinger Bạn nói đúng. Nhưng nếu hình ảnh được sửa đổi và được lưu trữ dưới dạng hình thu nhỏ. Tôi đoán điều này sẽ hữu ích. :) – 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

Khi tôi đang cố gắng này, tôi nhận được '*** LoạiError: không thể thoát dụ để binary' – user208859

+0

đúc dữ liệu để' psycopg2.Binary' là chìa khóa cho tôi, cảm ơn! – Matt

0

đó là giải pháp của tôi, nó có thể làm việc trong trang web của tôi:

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

sử dụng Flask, Flask-Alchemy để xử lý cơ sở dữ liệu.

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

đó là tệp html.you có thể nhúng nó vào html của bạn.

Các vấn đề liên quan