【问题标题】:How to save a image file on a Postgres database?如何将图像文件保存在 Postgres 数据库中?
【发布时间】:2013-05-26 21:38:08
【问题描述】:

出于学习目的,我正在使用 Python+Flask 创建一个站点。我想从数据库中恢复图像并将其显示在屏幕上。但一步一步。

我不知道如何将图像保存在我的数据库中。我的搜索只显示我必须在我的数据库中使用bytea 类型。然后我得到我的图像并以某种方式(??)将其转换为字节数组(bytea == 咬数组?)并以某种方式(??)在插入命令中使用该数组。

我能够发现(也许)如何在 Java (here) 和 C# (here) 中做到这一点,但我真的很想使用 Python,至少现在是这样。

有人可以帮我吗?

这个网站上有很多此类问题。但他们中的大多数(轻松超过 85%)被回答为“您不应该将图像保存在数据库中,它们属于 fs”并且无法回答问题。其余的并不能完全解决我的问题。因此,如果重复项有这种答案,请不要将其标记为重复项。

【问题讨论】:

  • 除了 bytea,还有使用“大对象”的选项。 Here is a list of options with links to the manual. 虽然没有 Python 特定的解决方案。
  • 好的,没有特定的 Python。所以一般来说,我与图像有什么关系?获取文件并将其转换为字符串?获取字符串并将其设为二进制?我不明白的是你的 fs 中的“image.jpg”和它的 bytea 数据之间会发生什么。

标签: python database image postgresql bytea


【解决方案1】:

我通常不会为人们编写完整的示例程序,但你并没有要求它,而且它是一个非常简单的,所以你去吧:

#!/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 用于直接写入文件,以及大对象读取函数用于读取小对象一次图像块。

【讨论】:

  • 太棒了!这正是我一直在寻找的!按照先前的答案,我已经设法在数据库中插入了一个文件,但是在尝试恢复它时我仍然迷路了。当我使用 Python 2.7 时,我将不得不使用一个缓冲区对象,就像你说的那样,但它们看起来使用起来很复杂!我会对此做一些研究。谢谢,这真的很有帮助!
  • @FelipeMatos 另外,请记住,虽然上面的示例将从数据库加载的图像保存到文件中,但您可以轻松地将其从缓冲区加载到 PIL 图像中进行显示,然后发送到 http 客户端等。您很少需要将其写入磁盘 - 如果这样做,您通常会使用 tempfile.TemporaryFile
【解决方案2】:

我希望这对你有用。

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

【讨论】:

  • 好的,我试过了,几乎成功了。为了将来参考,我首先从here 安装了 Python Image Library。我能够使用您的代码运行查询(这是一个很好的信号),但我的数据库是 UFT-8,所以我发现了编码问题。在对编码进行了一些研究之后,我发现(惊喜!)psycopg 支持这种操作,对here。我设法按照这些步骤插入条目,现在我必须找出如何恢复它。
  • 在PIL中不需要实际加载图像,您只需要读取文件并将其存储在数据库中。尽管如此,+1 是一个有用的例子。
  • @CraigRinger 你是对的。但是如果图像被修改并存储为缩略图。我想这会很有用。 :)
【解决方案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()

【讨论】:

  • 当我尝试这个时,我得到*** TypeError: can't escape instance to binary
  • 将数据投射到psycopg2.Binary 是我的关键,谢谢!
【解决方案4】:

这是我的解决方案,它可以在我的网站上运行:

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

使用 Flask,Flask-Alchemy 处理数据库。

{% 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 中。

【讨论】:

    【解决方案5】:

    您可以使用 Python 的 base64 将任意二进制字符串编码和解码为文本字符串。

    【讨论】:

    • 你可以,但这不是正确的答案。数据库有效地将二进制数据存储在bytea字段中,因此完全不需要对其进行base64编码。
    猜你喜欢
    • 2014-12-11
    • 1970-01-01
    • 2015-01-08
    • 2013-02-09
    • 2015-05-30
    • 2018-04-01
    • 2020-03-03
    • 2013-10-04
    • 1970-01-01
    相关资源
    最近更新 更多