Intro 

This article is a spiritual successor to lazyload. You might want to read this first. To recap: We have designed a javascript routine, which will load images based on the view-ports size and position. Previously we have pre-generated those images and returned specific resolutions as static content, now we want to automatically scale pictures according to URL arguments

Flask Route 

First we need a flask route, note that if you are using the default static location, you cannot send files from outside the ‘static’-directory.

# unset default static directory if you want to
app = flask.Flask("NAME", static_folder=None)

@app.route("/picture/<path:path>")
def sendPicture(path):
    response = flask.send_from_directory(...)
    return response

Picture Generation Function 

The function which will generate the image with the correct settings from the original and save it to a caching path which the function will return.

We use the python module pillow (aka PIL) to resize and re-encode the images and then return the path to the newly generate image. The actual resizing and re-encoding is done by PIL.Image.thumbnail.

CACHE_DIR = "someotherdir/"

import PIL.Image
def generatePicture(pathToOrig, scaleX, scaleY, encoding):
    '''Generate an image with the requested scales and encoding if it doesn't already exist'''

    # create a cache dir if it doesn't already exist #    
    if os.path.isfile(CACHE_DIR):
        raise OSError("Picture cache dir name is occupied by a file!")
    if not os.path.isdir(CACHE_DIR):
        os.mkdir(CACHE_DIR)

    # use same encoding if none is given # 
    filename, extension = os.path.splitext(os.path.basename(pathToOrig))
    if not encoding:
        encoding = extension.strip(".")

    # fix some extensions PILLOW doesn't like #
    if encoding.lower() == "jpg":
        encoding = "jpeg"

    # open image #
    image = PIL.Image.open(os.path.join(PICTURES_DIR, pathToOrig))

    # ensure sizes are valid #
    x, y = image.size
    if not scaleY:
        scaleY = y
    scaleX = min(x, scaleX)
    scaleY = min(y, scaleY)

    # generate new paths #
    FILE_FORMAT = "x-{x}-y-{y}-{fname}.{ext}"
    newFile = FILE_FORMAT.format(x=scaleX, y=scaleY, fname=filename, ext=encoding)
    newPath = os.path.join(CACHE_DIR, newFile)

    # save image with new size and encoding #
    image.thumbnail((scaleX, scaleY), PIL.Image.ANTIALIAS)
    image.save(newPath, encoding)

    # return the new path # 
    return newPath

Argument Processing 

Now we extend the picture-route from above. The following assumes an URL looking like this:

https://example.com/picture.jpg?scaleX=NUMBER&scaleY=NUMBER&encoding=webp

The <path:path> maps the path after picture to the path argument. In addition we evaluate the URL-arguments…

scaleY = flask.request.args.get("scaley")
scaleX = flask.request.args.get("scalex")
scaleY = round(float(scaleY))
scaleX = round(float(scaleX))
encoding = flask.request.args.get("encoding")

..create the scaled picture…

path = generatePicture(path, scaleX, scaleY, encoding)

..and make a response:

raw = flask.send_from_directory(PICTURES_DIR, path, cache_timeout=3600)
response = flask.make_response(raw)
# set additional header/response parameters here
return response

And that’s it, now our picture-route supports automatic scaling and encoding! As I said in the beginning, you might want to read lazyload for an idea on how to deploy this to your website.

Links 


Feel free to send me a mail to share your thoughts!