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)

1 2

1 ответ:

Я не знаю Cherrypy, но я предполагаю, что вы посылаете в качестве аргумента PIL.Образ объекта для s1 и s1 не может сделать то, что он должен сделать, потому что он не распознает такого рода объект.