PIL ─ Python Imaging Library ─ загрузка миниатюр изображений в базу данных SQLAlchemy через CherryPy
# ! /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 из локального файла клиента. Загрузка происходит с помощью HTML-формы на сервер CherryPy. Затем изображение обрабатывается с помощью библиотеки Python Imaging Library (PIL) для получения миниатюры. Наконец, результат должен быть сохранен в базе данных SQLAlchemy, что не удалось, вероятно, из-за миниатюры.
Примечание: я пытаюсь сделать это, не сохраняя миниатюру временно в файле. Так как данные уже есть доступный в оперативной памяти, мне не нравится идея сохранения его в папке, затем добавления его в базу данных и, наконец, удаления его из папки. Просто мне кажется, что это неправильно.
Решение в конце в правке 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
Ниже приводится соединение SQLAlchemy с базой данных PostgreSQL.
Это хранится в сеансе CherryPy как "сеанс" и извлекается как s1.
Просто чтобы никто не перепутал s1 с вишневым объектом.
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 Traceback:
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
Во второй попытке я попытался вставить по крайней мере первоначально загруженный файл напрямую, чтобы проверить наличие каких-либо изменений.
Минималистский Код 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 Traceback:
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 или нет из миниатюры изображения PIL в двоичный файл?
Или, может быть, буфер, bytearray ... Я даже не знаю, что мне на самом деле нужно.
ByteArray cast caused:
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 (бывшее звено )
- Обработка Файлов CherryPy
Может быть, есть лучший инструмент / библиотека для этого?
Тот, который создает только миниатюры?
Править 1:
Я провел небольшое исследование о том, как сохранить объект изображения в потоке.Но сначала я попробовал пил.Изображение.функция tostring ():
thumb = image.copy()
thumb.thumbnail((30000, 300,), Image.ANTIALIAS)
thumb.tostring()
image_entry['thumbnail'] = thumb.copy()
Затем я попробовал модуль BytesIO ().
Это вызвало остановку работы: fileno PIL
Поскольку это известная ошибка в PIL, Я заменил PIL его вилочной подушкой и попробовал снова:
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
Исправлено в правке 1, это SQLAlchemy TypeError: не может экранировать экземпляр в двоичный
Это произошло только потому, что я попытался сохранить разнесенное значение в база данных:
"""
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()
Теперь, по крайней мере, очень длинный шестнадцатеричный код вставлен в таблицу, может быть, я смогу его использовать.Похоже, однако, что изображение и столбец миниатюр содержат один и тот же шестнадцатеричный код.
Править 4:
Изображение и миниатюра не содержал тот же самый шестнадцатеричный код, что и SQL-запрос, подтвержденный позже.
Только первые 1179 и последние 4 символа были одинаковыми, и я только что проверил в начале.
В промежутках содержание было разным, как и длина каждой записи.
Наконец, следует весь фрагмент кода как один.
Первый необходимый импорт:
from io import BytesIO
import base64
import cherrypy
import sqlalchemy
from sqlalchemy.orm import sessionmaker
from PIL import Image
Второй двигатель и сессия:
pg = sqlalchemy.create_engine(
'postgresql://{}:{}@{}:{}/{}'.format(
user, password, server, port, data))
Session = sessionmaker(bind=pg)
cherrypy.session['session'] = Session
Третий код загрузки изображения и миниатюр:
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, за упоминание опции save-to-stream,
и спасибо людям, которые предоставили эти ресурсы:
-переменные и файлы Python (simple io.Bytesio () пример)
- python Image PIL to binary Hex (упоминает UnsupportedOperation: fileno)