为啥在 PIL(low) 中读取图像会破坏 Flask 图像端点?

Posted

技术标签:

【中文标题】为啥在 PIL(low) 中读取图像会破坏 Flask 图像端点?【英文标题】:Why does reading in an image in PIL(low) break Flask image end point?为什么在 PIL(low) 中读取图像会破坏 Flask 图像端点? 【发布时间】:2015-03-19 15:49:04 【问题描述】:

我正在使用 Python Flask framework 构建网站。由于我将上传的图像存储在 MongoDB 中,因此我构建了一个简单的端点来按 id 提供图像:

@app.route('/doc/<docId>')
def getDoc(docId):
    userDoc = UserDocument.objects(id=docId).first()
    if not userDoc:
        return abort(404)

    return Response(userDoc.file_.read(), mimetype=userDoc.file_.content_type)

这很好用。但由于图像通常非常大,我现在希望能够提供原始图像的缩略图。所以使用Pillow 我想调整图像大小,将它们存储/缓存在/tmp 并在需要时提供它们。所以我从这个开始:

@app.route('/doc/<docId>')
def getDoc(docId):
    userDoc = UserDocument.objects(id=docId).first()
    if not userDoc:
        return abort(404)

    desiredWidthStr = request.args.get('width')
    desiredHeightStr = request.args.get('height')
    if desiredWidthStr or desiredHeightStr:    
        print 'THUMBNAIL'
        # Load the image in Pillow
        im = Image.open(userDoc.file_)  # <=== THE PROBLEM!!!
        # TODO: resize and save the image in /tmp

    print 'NORMAL'
    return Response(userDoc.file_.read(), mimetype=userDoc.file_.content_type)

当我现在注释掉问题所在的行并打开首页(加载了几张图片)时,所有图片都加载正常,我看到了这一点(如预期的那样):

THUMBNAIL
NORMAL
THUMBNAIL
NORMAL
THUMBNAIL
NORMAL
212.xx.xx.xx - - [2015-03-19 16:57:02] "GET /doc/54e74956724b5907786e9918?width=100 HTTP/1.1" 200 139588 0.744827
212.xx.xx.xx - - [2015-03-19 16:57:03] "GET /doc/54e7495c724b5907786e991b?width=100 HTTP/1.1" 200 189494 1.179268
212.xx.xx.xx - - [2015-03-19 16:57:03] "GET /doc/5500c5d1724b595cf71b4d49?width=100 HTTP/1.1" 200 264593 1.416928

但是当我运行上面粘贴的代码时(问题行没有注释),图像没有加载,我在终端中看到了这个:

THUMBNAIL
THUMBNAIL
THUMBNAIL
NORMAL
NORMAL
NORMAL
212.xx.xx.xx - - [2015-03-19 16:58:11] "GET /doc/54e74956724b5907786e9918?width=100 HTTP/1.1" 200 138965 0.657734
212.xx.xx.xx - - [2015-03-19 16:58:11] "GET /doc/54e7495c724b5907786e991b?width=100 HTTP/1.1" 200 188871 0.753112
212.xx.xx.xx - - [2015-03-19 16:58:11] "GET /doc/5500c5d1724b595cf71b4d49?width=100 HTTP/1.1" 200 257495 1.024860

除了这些,我在终端中看不到任何错误。当我尝试在浏览器中加载直接 url 时,它显示The image cannot be displayed because it contains errors.。我现在想知道两件事:

    尽管所有请求似乎都成功了 (200),但我在浏览器中看不到图像。为什么? 即使图像需要一段时间才能加载到 Pillow 中(我认为不会),我仍然希望图像最终能够加载。

有人知道为什么我将图像加载到 Pillow 中时没有提供这些图像吗?欢迎所有提示!

【问题讨论】:

我认为问题在于 Image.open 抛出了一个异常,该异常在某处被捕获并且没有报告给控制台。您是否尝试过使用with open('some/local/file.jpg') as f: Image.open(f)?看看是否打印错误。 @JDavidSmith - 嗯,你说得有道理。如果我这样做im = Image.open('/tmp/some_image_file.jpg'),图像会很好地服务。但我也在控制台中尝试了im = Image.open(userDoc.file_),效果很好(包括调整大小并保存在不同的位置)。另外:它最后会打印出NORMAL,所以它似乎可以很好地结束。但这确实表明 MongoDB 和 Pillow 的组合存在问题。我现在正在研究更多。 尝试将语句包装在try: ... except Exception as e: print(e) raise e 中。这将打印任何错误并重新引发它(在修复格式后,因为无法在 cmets 中执行换行符) @JDavidSmith - 试过了;根本没有错误输出。我越来越认为它与 MongoDB GridFS 文件存储有关。还是您有其他想法? 我想我刚刚注意到了这个问题:Image.open 将读取文件的全部内容,然后你 return Response(userDoc.file_.read(), ...)。由于文件已经被读取,该函数返回b"",这是一个无效的图像。 【参考方案1】:

您对 Pillow 没有任何问题。您的问题是您正在提供空响应。如果您提供的是缩略图,则要求 Pillow 阅读文件

if desiredWidthStr or desiredHeightStr:    
    print 'THUMBNAIL'
    im = Image.open(userDoc.file_)  # reads from the file object

然后您尝试从该相同的文件对象提供服务

if desiredWidthStr or desiredHeightStr:    
    print 'THUMBNAIL'
    # Load the image in Pillow
    im = Image.open(userDoc.file_)  # <=== THE PROBLEM!!!
    # TODO: resize and save the image in /tmp

return Response(userDoc.file_.read(), mimetype=userDoc.file_.content_type)

这里的userDoc.file_.read() 最多只能返回部分图像,因为Image.open() 已经移动了文件指针。这取决于图像类型实际读取了多少以及图像指针所在的位置。

添加一个 file.seek() 电话,你会看到你的图像重新出现:

if desiredWidthStr or desiredHeightStr:    
    print 'THUMBNAIL'
    # Load the image in Pillow
    im = Image.open(userDoc.file_)

print 'NORMAL'
userDoc.file_.seek(0)  # ensure we are reading from the start
return Response(userDoc.file_.read(), mimetype=userDoc.file_.content_type)

【讨论】:

以上是关于为啥在 PIL(low) 中读取图像会破坏 Flask 图像端点?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用 PIL 和 pytorch 对图像进行双线性缩放会产生不同的结果?

为啥 javafx 会破坏我的半透明游标?

opencv和PIL读取图片的速度对比

opencv和PIL读取图片的速度对比

opencv和PIL读取图片的速度对比

为啥 Python 不能从 PIL 导入图像?