Skip to main content
 首页 » 编程设计

python之PIL─Python Imaging Library─通过CherryPy将图像缩略图上传到SQLAlchemy数据库

2025年05月04日14mate10pro

# ! /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数据库中,该数据库可能由于缩略图而失败。

注意:我尝试执行此操作而不将缩略图临时保存在文件中。由于数据已经在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连接。
它作为“ session”存储在CherryPy会话中,并作为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 


在第二次尝试中,我尝试至少直接插入最初上传的文件,以检查是否有任何更改。

极简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映像中的实例或PIL映像中的None实例转换为二进制文件?
或者也许是缓冲区,字节数组……我什至不知道我真正需要什么。

字节数组强制转换导致:

  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( former link)
- 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中的一个已知错误,因此我用其Pillow叉替换了PIL,然后再次尝试:

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


都让我遇到了SQLAlchemy TypeError:无法将实例转义为二进制文件
当然,就像错误追溯到之前一样:

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


最后,我用StringIO.StringIO()替换了BytesIO(),但是并没有改变任何事情。
我猜这更多是一个特殊的SQLAlchemy问题。

编辑2:

在我错误地提到未知的SQLAlchemy TypeError之前:无法将Part转换为二进制
在EDIT 1中进行了更正,它是SQLAlchemy TypeError:无法将实例转义为二进制
发生这种情况仅是因为尝试过将POSTed值保存在数据库中:

"""  
    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ţuOvidiu Gabriel提到了“保存到流”选项,
感谢提供这些资源的人们:

- Python variables and files(简单的io.BytesIO()示例)
- python Image PIL to binary Hex(提及UnsupportedOperation:fileno)

请您参考如下方法:

我不知道Cherrypy,但我的猜测是您要向s1发送一个PIL.Image对象作为参数,而s1无法执行它必须做的事情,因为他无法识别这种对象。