Blog

Alter an image to send to a web page

Posted July 25 2024

Recently for this website, I needed to find a way to artificially resize the images on the gallery and album page on the server so I can send a lower resolution version of the image to the client to reduce loading times for the client and reduce the amount of data the server has to send.

After searching online I quickly found that there seemed to be nothing showing how to do this, so I had to try and find a way to do it myself, since I knew it was possible because it exists on CDN's I knew all I had to do was figure it out.

So through some looking into the web framework I use, when serving a file such as an image to the client, it just uses the default python open function to read the file and send it in a HTTP response.

After looking on how to resize an image I came across Pillow, a Python library to alter images. Now, since I knew how to resize the image, I needed to figure out a way to convert this Pillow image object into a readable file that the browser can render as an image.

This was the most difficult part of trying to figure this whole thing out, but here is the code required to do it, firstly here is a separate piece of code that is required.

from PIL import Image
import io


def is_valid_image_pillow(file_name):
    try:
        with Image.open(file_name) as img:
            img.verify()
            return True
    except (IOError, SyntaxError):
        return False

And here is the code in the function to serve the static files to the client:

# Check if the file is an image
if is_valid_image_pillow(filename):
    # Instantiate the bytes object 
    output = io.BytesIO()
    new_file = filename
    # parameters is a function argument I added to get the URL parameters, so this syntax can be changed to whatever the syntax is for the framework you use.
    if w := parameters.width:
        width = int(w)
    if h := parameters.height:
        height = int(h)
    if m := parameters.method:
        resize_method = m

    # open the image file, creating an image object
    image_object = Image.open(new_file)
    image_width, image_height = image_object.size
    if width or height:
        if resize_method == 'exact':
            image_object.resize((width or image_width, height or image_height))
        elif resize_method == 'maintain' or not resize_method:
            image_object.thumbnail((width or image_width, height or image_height))
    # Save the resized image object to the bytes object from before
    image_object.save(output, image_object.format)

    # Convert the bytes object value to a BytesIO object (yes this is required), then convert that to a buffered reader that can be read by the client browser
    image_file = io.BufferedReader(io.BytesIO(output.getvalue()))
else:
    # Perform the normal file open if it isn't an image
    image_file = open(filename, 'rb')

body = '' if request.method == 'HEAD' else image_file