# ! /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 について言及)