使用 Python Bottle 上传后的图像处理

Image processing after upload with Python Bottle

上下文

我制作了一个简单的网络应用程序,用于将内容上传到博客。前端向 Python 3.7 上的 Bottle 运行 后端发送 AJAX 请求(使用 FormData)。文本内容保存到 MySQL 数据库中,图像保存到服务器上的文件夹中。一切正常。

图像处理和PIL/Pillow

现在,我想启用对上传图像的处理以使其标准化(我需要将它们全部调整大小 and/or 裁剪为 700x400 像素)。

我希望为此使用 Pillow。我的问题是从 Bottle 中的文件对象创建一个 PIL Image 对象。我无法初始化有效的图像对象。

代码

# AJAX sends request to this route
@post('/update')
def update():
    # Form data
    title = request.forms.get("title")
    body = request.forms.get("body")
    image = request.forms.get("image")
    author = request.forms.get("author")
    
    # Image upload
    file = request.files.get("file")
    if file:
        extension = file.filename.split(".")[-1]
        if extension not in ('png', 'jpg', 'jpeg'):
            return {"result" : 0, "message": "File Format Error"}
        save_path = "my/save/path"
        file.save(save_path)

问题

这一切都按预期工作,但我无法使用 pillow 创建有效的 Image 对象进行处理。我什至尝试使用保存路径重新加载保存的图像,但这也不起作用。

其他尝试

下面的代码无效。它导致了内部服务器错误,尽管我在设置更详细的 Python 调试时遇到了问题。

path = save_path + "/" + file.filename
image_data = open(path, "rb")
image = Image.open(image_data)

手动记录时,path 是一个有效的亲戚 URL ("../domain-folder/images"),我已经检查过我确实使用 [=20= 正确导入了 PIL (Pillow) ].

我试过改编 :

image = Image.frombytes('RGBA', (128,128), image_data, 'raw')

但是,在创建 Image 对象之前我不知道大小。我也尝试使用 io:

image = Image.open(io.BytesIO(image_data))

这也没有用。在每种情况下,只有试图初始化 Image 对象的行会导致问题。

总结

Bottle 文档说上传的文件是一个类似文件的对象,但我在创建一个我可以处理的 Image 对象方面没有取得多大成功。

我该怎么办?我对保存之前或之后的处理没有偏好。我对处理感到满意,它正在初始化导致问题的 Image 对象。


编辑 - 解决方案

我通过改编 eatmeimadanish 的答案使它起作用。我不得不使用 io.BytesIO 对象从 Bottle 中保存文件,然后从那里用 Pillow 加载它。处理完成后,可以按常规方式保存。

obj = io.BytesIO()
file.save(obj) # This saves the file retrieved by Bottle to the BytesIO object
path = save_path + "/" + file.filename

# Image processing
im = Image.open(obj) # Reopen the object with PIL
im = im.resize((700,400))
im.save(path, optimize=True)

我从 Pillow documentation 中找到了一个可能有用的不同函数。

PIL.Image.frombuffer(mode, size, data, decoder_name='raw', *args)

Note that this function decodes pixel data only, not entire images. If you have an entire image file in a string, wrap it in a BytesIO object, and use open() to load it.

据我了解,您正在尝试在将图像保存到本地后调整其大小(请注意,您可以尝试在保存之前调整大小)。如果这是你想在这里实现的,你可以直接使用 Pillow 打开图像,它会为你完成工作(你不必 open(path, "rb"):

image = Image.open(path)
image.resize((700,400)).save(path)

改为使用 StringIO。

From PIL import Image
try:
    import cStringIO as StringIO
except ImportError:
    import StringIO

s = StringIO.StringIO()
#save your in memory file to this instead of a regular file
file = request.files.get("file")
if file:
    extension = file.filename.split(".")[-1]
    if extension not in ('png', 'jpg', 'jpeg'):
        return {"result" : 0, "message": "File Format Error"}
    file.save(s)
im = Image.open(s)
im.resize((700,400))
im.save(s, 'png', optimize=True)
s64 = base64.b64encode(s.getvalue())