Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does reading in an image in PIL(low) break Flask image end point?

I'm using the Python Flask framework to build a website. Since I'm storing uploaded images in MongoDB, I built a simple endpoint to serve the images by 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)

This works perfectly well. But because the images are often very large I now want to be able to also serve up thumbnails of the original images. So using Pillow I want to resize the images, store/cache them in /tmp and serve them up when needed. So I started off with this:

@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)

When I now comment out the line with the problem and open the front page (which loads a couple of images) all images load fine and I see this (as expected):

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

but when I run the code as I pasted it above (with the problem line not commented) the images don't load, and I see this in the terminal:

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

Except for these things I see no error whatsoever in the terminal. When I try to load a direct url in the browser it says The image cannot be displayed because it contains errors.. I now wonder about two things:

  1. Even though all requests seem to be a success (200) I don't see the images in the browser. Why?
  2. Even if it would take a while for the image to be loaded into Pillow (which I think it doesn't), I would still expect the images to load in the end.

Does anybody know why the images are not served when I load them into Pillow? All tips are welcome!

like image 623
kramer65 Avatar asked Jan 08 '23 17:01

kramer65


1 Answers

You don't have any problems with Pillow. Your problem is that you are serving an empty response. If you are serving a thumbnail, you ask Pillow to read the file:

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

You then try and serve from that same 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)

The userDoc.file_.read() here will return, at best, a partial image since Image.open() has already moved the file pointer. It depends on the image type how much is actually read and where the image pointer is located at by that time.

Add in a file.seek() call and you'll see your image re-appear:

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)
like image 122
Martijn Pieters Avatar answered Jan 17 '23 11:01

Martijn Pieters