Introduction
The Images API enables you to perform common transforms on images like resizing, rotating and adjusting their color and contrast. These transforms generally happen in the context of a user uploaded image or photo. This document describes the process of uploading, transforming, storing and serving images dynamically. We'll use the Guestbook example from the getting started guide and modify it to let a user upload an avatar with their greeting.
Creating an Image Property
The first thing we need to do is update the model from the guestbook sample to store the uploaded image as a blob.
class Greeting(db.Model): author = db.StringProperty() content = db.StringProperty(multiline=True) avatar = db.BlobProperty() date = db.DateTimeProperty(auto_now_add=True)
Uploading User Images
Next we'll modify the form to add another field that enables the user to select a file from their computer to upload. We'll also have to add the
enctype
attribute to the form tag to specify that we want this to be a multi-part form post.
self.response.out.write(""" <form action="/sign?%s" enctype="multipart/form-data" method="post"> <div><textarea name="content" rows="3" cols="60"></textarea></div> <div><input type="submit" value="Sign Guestbook"></div> </form> <hr> <form>Guestbook name: <input value="%s" name="guestbook_name"> <div><label>Avatar:</label></div> <div><input type="file" name="img"/></div> <input type="submit" value="switch"></form> </body> </html>""" % (urllib.urlencode({'guestbook_name': guestbook_name}), cgi.escape(guestbook_name)))
This should give us a simple form with two fields.
At this point we are also going to want to update the Guestbook handler to get the image data from the form post and store it as a Blob in the datastore.
class Guestbook(webapp2.RequestHandler): def post(self): guestbook_name = self.request.get('guestbook_name') greeting = Greeting(parent=guestbook_key(guestbook_name)) if users.get_current_user(): greeting.author = users.get_current_user().nickname() greeting.content = self.request.get('content') avatar = self.request.get('img') greeting.avatar = db.Blob(avatar) greeting.put() self.redirect('/?' + urllib.urlencode( {'guestbook_name': guestbook_name}))
Transforming Images
For the Guestbook application we are going to create 32x32 avatars. First we have to import the
google.appengine.api.images
module. Then all we have to do is call the
resize
function and pass in the image data.
from google.appengine.api import images class Guestbook(webapp2.RequestHandler): def post(self): guestbook_name = self.request.get('guestbook_name') greeting = Greeting(parent=guestbook_key(guestbook_name)) if users.get_current_user(): greeting.author = users.get_current_user().nickname() greeting.content = self.request.get('content') avatar = images.resize(self.request.get('img'), 32, 32) greeting.avatar = db.Blob(avatar) greeting.put() self.redirect('/?' + urllib.urlencode( {'guestbook_name': guestbook_name}))
Dynamically serving images
Finally we have to create a Image handler that will dynamically serve these images off the
/img
path. We'll also update the HTML to pull in these dynamically served images.
class Image(webapp2.RequestHandler): def get(self): greeting = db.get(self.request.get('img_id')) if greeting.avatar: self.response.headers['Content-Type'] = 'image/png' self.response.out.write(greeting.avatar) else: self.error(404)
In the Image Handler we get the
img_id
from the request. We'll need to update the HTML of the Guestbook to pass the key of the greeting to the Image handler
self.response.out.write('<div><img src="img?img_id=%s"></img>' % greeting.key()) self.response.out.write('<blockquote>%s</blockquote></div>' % cgi.escape(greeting.content))
We now have completed our modified Guestbook application:
import cgi import datetime import urllib import webapp2 from google.appengine.ext import db from google.appengine.api import images from google.appengine.api import users class Greeting(db.Model): """Models a Guestbook entry with an author, content, avatar, and date.""" author = db.StringProperty() content = db.StringProperty(multiline=True) avatar = db.BlobProperty() date = db.DateTimeProperty(auto_now_add=True) def guestbook_key(guestbook_name=None): """Constructs a Datastore key for a Guestbook entity with guestbook_name.""" return db.Key.from_path('Guestbook', guestbook_name or 'default_guestbook') class MainPage(webapp2.RequestHandler): def get(self): self.response.out.write('<html><body>') guestbook_name=self.request.get('guestbook_name') greetings = db.GqlQuery('SELECT * ' 'FROM Greeting ' 'WHERE ANCESTOR IS :1 ' 'ORDER BY date DESC LIMIT 10', guestbook_key(guestbook_name)) for greeting in greetings: if greeting.author: self.response.out.write( '<b>%s</b> wrote:' % greeting.author) else: self.response.out.write('An anonymous person wrote:') self.response.out.write('<div><img src="img?img_id=%s"></img>' % greeting.key()) self.response.out.write('<blockquote>%s</blockquote></div>' % cgi.escape(greeting.content)) self.response.out.write(""" <form action="/sign?%s" enctype="multipart/form-data" method="post"> <div><textarea name="content" rows="3" cols="60"></textarea></div> <div><label>Avatar:</label></div> <div><input type="file" name="img"/></div> <div><input type="submit" value="Sign Guestbook"></div> </form> <hr> <form>Guestbook name: <input value="%s" name="guestbook_name"> <input type="submit" value="switch"></form> </body> </html>""" % (urllib.urlencode({'guestbook_name': guestbook_name}), cgi.escape(guestbook_name))) class Image(webapp2.RequestHandler): def get(self): greeting = db.get(self.request.get('img_id')) if greeting.avatar: self.response.headers['Content-Type'] = 'image/png' self.response.out.write(greeting.avatar) else: self.response.out.write('No image') class Guestbook(webapp2.RequestHandler): def post(self): guestbook_name = self.request.get('guestbook_name') greeting = Greeting(parent=guestbook_key(guestbook_name)) if users.get_current_user(): greeting.author = users.get_current_user().nickname() greeting.content = self.request.get('content') avatar = images.resize(self.request.get('img'), 32, 32) greeting.avatar = db.Blob(avatar) greeting.put() self.redirect('/?' + urllib.urlencode( {'guestbook_name': guestbook_name})) app = webapp2.WSGIApplication([('/', MainPage), ('/img', Image), ('/sign', Guestbook)], debug=True)