Sunday, October 23, 2011

Weekend Project: Image Thumbnailer - Day 2

Day 2

6:18 am - Rendering output as JSON

Javascript Object Notation (JSON) is almost expected nowadays when deploying an API. Pyramid has a convenient approach to doing this, by providing a renderer called 'json'.
I edited my view information under 'imageuploadr/' so that it now looks like this:

The result is that the response is rendered as JSON.
When viewed using Chrome the output is displayed in the browser. If you use Firefox you will, most likely, be prompted to download the result as a text file.
 Here is how it looks in Chrome.

9:05 am  - File uploads

After lots of tangents reading up on the features of Pyramid, I've been able to track down a useful resource on working with files. I haven't tried this yet and may have to put it off for a few hours but this is next on my reading list:
  • Added a simple form to my template
  • Switched the renderer from json back to my template
The new form code was added templates/ and looks like this:
  <form action="/process_image" method="post" accept-charset="utf-8"
    <label for="image">Image</label>
    <input id="image" name="image" type="file" value="" />
    <input type="submit" value="submit" />
Note the action 'process_image' which will call a view called process_image does not exist yet (adding it to my todo list).
Here's a screenshot:

Many things to do and get done

So no coding until this evening.

9:33 pm - Implementing process_image

In order to support image processing I decided to make use of the standard library uuid for generation of unique names and Pillow (a setuptools friendly, fork of the Python Imaging Library).

 Since Pillow is not part of the standard library I had to list it as a dependency by adding it to the requires list of the file.
requires = ['pyramid', 'pyramid_debugtoolbar','Pillow']
Then I had to re-run the develop command:
python develop
  My file now has 3 new functions (only one of them 'process_image' will become a new view though. I also decided to use the @view_config decorators that Pyramid provides, the resulting code:
import os
import uuid
from os.path import basename, splitext
from pyramid.response import Response
from pyramid.view import view_config
from PIL import Image

def my_view(request):
    return {'project':'imageuploadr',

def process_image(request):
    filename = request.POST['image'].filename

    input_file = request.POST['image'].file
    # I generate a new file name using uuid
    newname = str(uuid.uuid1()) + splitext(filename)[-1]
    file_path = os.path.join('/tmp', newname)
    output_file = open(file_path, 'wb')

    # write the data to the output file
    while 1:
        data =<<16)
        if not data:

    new_image = thumbnail_image(file_path)

    return {'f':filename,'newimage':new_image}

def thumbnail_image(image_path):
    thumbname = "/tmp/%s.thumb%s" % splitext(basename(image_path))
    image =
    new_image = image.resize((200, 200), Image.ANTIALIAS)
    return thumbname

 11:08 pm - Done for now, next steps

That's it, I'm sure I could have gotten this done faster if I didn't have other weekend obligations, and if I didn't spend time documenting my steps (but that would reduce the value of the exercise). The research was fun and it proves that I can get stuff done in the Pyramid Framework. The framework is predictable and well documented, I did overlook a few typos, like this sentence starting with 'THe'

on the page.
(update: The typo has been fixed at github)

What next

This is a list of other things I could do with this project.
  • An Ajax Style Interface
  • HTML5 Drag and Drop Upload
  • Mapping the uuid name to the original file name
  • A progress bar during upload
  • Multiple File Upload
  • Add user accounts and authentication
  • A user interface for cropping the uploaded image (or tie it to an api like pixlr or picnik)
  • A website screenshot taker

Closing Thoughts.

Coming from the school of traversal I still need to iron out url dispatch and view configuration. I had a few false starts trying to figure out whether to use @view_config(route_name="....") or @view_config(name="...."), but I managed to get something working with a bit of googling. I'll need to re-read the documentation.
The short project format is more engaging and helps me to learn much better than following the steps of a tutorial, I'll definitely need to do more of these. In the process I've been able to learn a bit more about:
  • Rendering to JSON and other formats with Pyramid
  • Managing File Uploads with Pyramid
  • Interacting with form requests and responses
  • Using form request data in templates
  • Basics of URL Dispatch and view configuration
  • Using the Pyramid Debug Toolbar
  • Processing a file based on a URL
Most useful resource: The Pyramid_Cookbook


Gaurav said...

thank you..!!

David B said...

You are welcome :)

Sign up for my upcoming Plone 5 Book & Video tutorials

plone 5 for newbies book and videos