Storing and Serving Images with AppEngine

Many web applications allow the user to post images such as a profile picture. Adding such functionality with App Engine is easy, and these notes provide the how-to.

Storing Images
Files are stored in an AppEngine Database using the BlobProperty. For instance, the following Account class stores a profile picture:

class Account(db.Model):
  name=db.StringProperty()
  user = db.UserProperty()
  profile_pic = db.BlobProperty()

HTML forms allow for inputs of type "file". Such an input type prompts the user to choose a file with a dialog. When file input is used, the enctype should be set to "multipart/form-data" and the method to "post". The following example let's the user choose an image file (and enter a name).

   <form action="/update_person" enctype="multipart/form-data" method="post">
      Name: <input type="text" value= {{account.name}} name="name" ><br/>
      Profile Picture: <input type="file" name="picfile">
      <input type="submit" value="Submit">
    </form>


Note that the input type is specified as "file", you must set the enctype as shown above, and the method must be specified as "post".

Here's a controller that handles the submission above:

class UpdatePersonHandler(webapp.RequestHandler):
  def post(self):
    name=self.request.get("name")
    pic = self.request.get("picfile")
    user = users.get_current_user()
    account_query = db.GqlQuery("Select * from Account where user=:1",user)
    account = account_query.get()
    if pic:
        account.profile_pic = db.Blob(pic) 

    account.name=name
    account.put()
    self.redirect('/profile')

Displaying Stored Images

Images stored as blobs won't be loaded as static files-- you'll need controller code to server the pictures from the files stored in the database.

In particular, you'll need to add a special controller like the following:

class ImageHandler (webapp.RequestHandler):
  def get(self):
    account_key=self.request.get('key')
    account = db.get(account_key)
    if account.profile_pic:
       self.response.headers['Content-Type'] = "image/png"
       self.response.out.write(account.profile_pic)
    else:
       self.error(404)


and you'll need to add a mapping:

        application = webapp.WSGIApplication(
                   
                                     [('/', MainPage),
                                      ('/imageit',ImageHandler)
                    
                                     ],
                                     debug=True)

The ImageHandler controller code expects the key of an account as a parameter. It uses that key to get the account object from the database. Note that the image we are showing might or might not be that of the current user. Thus we can't just get the account object from the current user.

Given theaccount object, ImageHandler then grabs the saved blob in account.profile_pic, and write's that file directly to the browser. Note that the content-type must be set.

Given such an image handler, an HTML template can display an image with code like the following:
      
    <img src='imageit?key={{account.key}}' alt='no image'></img>

The src of imageIt results in ImageHandler being called. {{account.key}} will return the account's key field as the parameter to the controller.
   

Recent site activity