2
# ! /usr/bin/env python
# -*- coding: utf-8 -*-
# image_upload.py

""" Python        2.7.3
    Cherrypy      3.2.2
    PostgreSQL    9.1
    psycopy2      2.4.5
    SQLAlchemy    0.7.10
    PIL           1.1.7
"""

クライアントのローカル ファイルから、画像とそのサムネイルを SQLAlchemy データベースに保存しようとしています。アップロードは、CherryPy サーバーへの HTML フォームで行われます。次に、画像は Python Imaging Library (PIL) で処理され、サムネイルが取得されます。最後に結果を SQLAlchemy データベースに保存する必要がありますが、おそらくサムネイルが原因で失敗しました。

注:サムネイルを一時的にファイルに保存せずにこれを実行しようとしています。データはすでに RAM で使用できるため、フォルダーに保存してからデータベースに追加し、最後にフォルダーから削除するという考えは好きではありません。そんなことをするのは正しくないと感じます。

EDIT 4の最後のソリューション

ObjectImage クラスのテーブルについて、
このまま使用する必要があります。
必須です!Python 2.7 と同様

column       type            attributes
----------------------------------------
id           int             PRIMARY KEY
object_id    int             REFERENCES Obeject(id) ON DELETE CASCADE
filename     varchar(252)    
image        bytea           
thumbnail    bytea           
preview      boolean         

以下は、PostgreSQL データベースへの SQLAlchemy 接続です。
これは CherryPy セッションに「session」として保存され、s1 として取得されます。
誰も s1 を CherryPy オブジェクトと混同しないように。

pg = sqlalchemy.create_engine(
        'postgresql://{}:{}@{}:{}/{}'.format(
            user, password, server, port, data))
Session = sessionmaker(bind=pg)
cherrypy.session['session'] = Session

最小限の Python コード:

""" 
    The variable "image_file"
    comes directly from the POSTed dictionary.
    image_file = kwargs['image_file']
"""

s1 = cherrypy.session.get('session')
image_entry = {}

img = StringIO.StringIO(image_file.file.read())
image = Image.open(img)
image_entry['image'] = image.copy()

thumb = image.copy()
thumb.thumbnail((30000, 300,), Image.ANTIALIAS)
image_entry['thumbnail'] = thumb.copy()

image_entry['object_id'] = chosen_one
image_entry['filename'] = image_file.filename
image_entry['preview'] = 't'

s1.add(ObjecteImage(**image_entry))
s1.commit()                          #line 1621

CherryPy トレースバック:

  File "image_upload.py", line 1621, in store_image
    s1.commit()
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/orm/session.py", line 710, in commit
    self.transaction.commit()
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/orm/session.py", line 368, in commit
    self._prepare_impl()
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/orm/session.py", line 347, in _prepare_impl
    self.session.flush()
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/orm/session.py", line 1734, in flush
    self._flush(objects)
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/orm/session.py", line 1805, in _flush
    flush_context.execute()
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/orm/unitofwork.py", line 331, in execute
    rec.execute(self)
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/orm/unitofwork.py", line 475, in execute
    uow
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/orm/persistence.py", line 64, in save_obj
    table, insert)
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/orm/persistence.py", line 558, in _emit_insert_statements
    execute(statement, params)
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/engine/base.py", line 1449, in execute
    params)
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/engine/base.py", line 1584, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/engine/base.py", line 1691, in _execute_context
    context)
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/engine/default.py", line 331, in do_execute
    cursor.execute(statement, parameters)
TypeError: can't escape instance to binary

2 回目の試行では、変更を確認するために、少なくとも最初にアップロードされたファイルを直接挿入しようとしました。

最小限の Python コード:

s1 = cherrypy.session.get('session')
image_entry = {}

img = StringIO.StringIO(image_file.file.read())
image = Image.open(img)
image_entry['image'] = image_file

[ ... the same as above ... ]

s1.add(ObjecteImage(**image_entry))
s1.commit()                          #line 1621

CherryPy トレースバック:

  File "image_upload.py", line 1621, in store_image
    s1.commit()
  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/orm/session.py", line 710, in commit
    self.transaction.commit()

  [ ... the same as above ... ]

  File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.10-py2.7-linux-x86_64.egg/sqlalchemy/engine/default.py", line 331, in do_execute
    cursor.execute(statement, parameters)
TypeError: can't escape Part to binary

インスタンスを PIL Image または None from PIL Image サムネイルからバイナリに変換するにはどうすればよいですか?
または、おそらくバッファ、バイト配列...実際に何が必要なのかさえわかりません。

バイト配列キャストが原因:

  File "image_upload.py", line 1611, in store_image
    image_entry['thumbnail'] = bytearray(thumb.copy())
  TypeError: iteration over non-sequence

バッファー キャストの原因:

  File "image_upload.py", line 1611, in store_image
    image_entry['thumbnail'] = buffer(thumb.copy())
  TypeError: buffer object expected

ここで答えが見つかりませんでした:
- Python Imaging Library Handbook (以前のリンク)
- CherryPy File Handling

これにはおそらくより良いツール/ライブラリがありますか?
サムネイルのみを作成するものですか?

編集1:

Image オブジェクトをストリームに保存する方法について少し調査しました。
しかし、最初に PIL.Image.tostring() 関数を試しました:

thumb = image.copy()
thumb.thumbnail((30000, 300,), Image.ANTIALIAS)
thumb.tostring()
image_entry['thumbnail'] = thumb.copy()

次に、BytesIO() モジュールを試しました。
UnsupportedOperation: fileno PIL が発生しました。これ
は PIL の既知のバグであるため、PIL をフォーク Pillow に置き換えて、もう一度試しました。

thumb = image.copy()
thumb.thumbnail((30000, 300,), Image.ANTIALIAS)
stream = BytesIO()
thumb.save(stream, "JPEG")
image_entry['thumbnail'] = stream.getvalue()

どちらも私に SQLAlchemy TypeError: can't escape instance to binary
もちろん、エラーが追跡される前のように:

s1.add(ObjectImage(**image_entry))
s1.commit()                          #line 1621

最後に、BytesIO() を StringIO.StringIO() に置き換えましたが、何も変わりませんでした。
これは、より特別な SQLAlchemy の問題だと思います。

編集2:

未知の SQLAlchemy TypeError: can't escape part to binary を間違って言及する前に
EDIT 1 で修正、それは SQLAlchemy TypeError: can't escape instance to binary
これは、POST された値をデータベースに保存しようとしたためにのみ発生しました:

""" 
    The variable "image_file"
    comes directly from the POSTed dictionary.
    image_file = kwargs['image_file']

    Alternative first line:
    img = StringIO.StringIO(image_file.file.read())
"""

img = BytesIO(image_file.file.read())
image = Image.open(img)
image_entry['image'] = image_file    # the dict value was meant be image.copy()

s1.add(ObjectImage(**image_entry))
s1.commit()                          #line 1621

編集3:


サムネイルがすでに正しい方法で「フォーマット」されている のに、フルサイズの画像をインスタンスとして挿入しようとしたのは間違いだったようです。

"""
    old version:
"""

img = BytesIO(image_file.file.read())
image = Image.open(img)
image_entry['image'] = image.copy()


"""
    new version:
"""

img = BytesIO(image_file.file.read())
image = Image.open(img)

fullsize = image.copy()
stream = BytesIO()
fullsize.save(stream, "JPEG")
image_entry['image'] =  stream.getvalue()


"""
    from EDIT 1:
"""

thumb = image.copy()
thumb.thumbnail((30000, 300,), Image.ANTIALIAS)
stream = BytesIO()
thumb.save(stream, "JPEG")
image_entry['thumbnail'] = stream.getvalue()

これで、少なくとも非常に長い HEX コードがテーブルに挿入されました。使用できる可能性があります。
ただし、画像とサムネイルの列には同じ HEX コードが含まれているようです。

編集4:

画像とサムネイルには、後で確認された SQL クエリと同じ HEX コードが含まれていませんでした。
最初の 1179 と最後の 4 文字だけが同じで、最初に確認したところです。
それぞれのエントリの長さのように、内容が異なっていました。
最後に、コード スニペット全体を 1 つにまとめます。

最初に必要なインポート:

from io import BytesIO
import base64
import cherrypy
import sqlalchemy
from sqlalchemy.orm import sessionmaker
from PIL import Image

2 番目のエンジンとセッション:

pg = sqlalchemy.create_engine(
        'postgresql://{}:{}@{}:{}/{}'.format(
            user, password, server, port, data))
Session = sessionmaker(bind=pg)
cherrypy.session['session'] = Session

3 番目の画像とサムネイルのアップロード コード:

s1 = cherrypy.session.get('session')
image_file = kwargs['image_file']

img = BytesIO(image_file.file.read())
image = Image.open(img)

fullsize = image.copy()
stream = BytesIO()
fullsize.save(stream, "JPEG")
image_entry['image'] =  stream.getvalue()

thumb = image.copy()
thumb.thumbnail((30000, 300,), Image.ANTIALIAS)
stream = BytesIO()
thumb.save(stream, "JPEG")
image_entry['thumbnail'] = stream.getvalue()

image_entry['sample'] = chosen_one
image_entry['filename'] = image_file.filename
image_entry['preview'] = 't'

s1.add(ObjectImage(**image_entry))
s1.commit()                          #line 1621

最後に、HTML のコードを取得する短いサムネイルを示します。

s1 = cherrypy.session.get('session')
qry = (s1.query(ObjectImage.id, ObjectImage.filename, ObjectImage.thumbnail).
    filter(ObjectImage.preview == 't'))

for rowX in qry:
    yield (u'<img src="data:image/jpeg; base64, {}" alt="thumbnail">'.
        format(base64.b64encode(rowX.thumbnail)))

パフォーマンス上の理由から、データ URI スキームを置き換える追加の関数を作成することを考えています。
しかし今のところ、ストリームに保存オプションについて言及してくれた Neaţu Ovidiu Gabriel と、
これらのリソースを提供してくれた人々に感謝します。

- Python 変数とファイル(単純な io.BytesIO() の例)
- python Image PIL からバイナリ Hex へ(UnsupportedOperation: fileno について言及)

4

1 に答える 1

1

Cherrypy についてはわかりませんが、引数として PIL.Image オブジェクトを s1 に送信していると思いますが、s1 はその種類のオブジェクトを認識しないため、必要な処理を実行できません。

于 2013-08-02T19:09:29.283 に答える