Google App Engineで動的にイメージを提供する

ついでに。

http://code.google.com/intl/ja/appengine/articles/images.html

はじめに

百聞は一見にしかず。視聴者へのアピールという点で画像が大きな役割を演じるというのは今日のウェブアプリケーションにおいても真実だ。Google App Engineでもデータストアを通じてすばやく簡単に画像を保存したり提供したりできる。PythonインターフェースにはデータモデリングAPIとGQL、our very own query languageが含まれる。この短い記事では、例としてMoview Review sample applicationのコードを使って、データストアAPIを画像を保存・提供するために使用する方法についての一通りを説明したい。

データストアに画像を保存

まずはじめに、データストアのオブジェクトとしてアプリケーションにそれぞれのムービーを保存したい。これはムービーをモデリングするクラスを定義することで実現できる。以下のMovieクラスではMovieオブジェクトが持つすべての属性が定義されている。定義される属性のひとつとして「picture」があるが、これがムービーのjpegイメージのバイナリデータを表している。画像を適切に扱うために、それらはBlobとして保存しなければならず、そのためにはpictureをBlobPropertyとして宣言しなければいけない。

Class Movie(db.Model)
  title = db.StringProperty()
  picture = db.BlobProperty(default=None)
  ...

これでモデルが定義されたので、このモデルを利用してムービーオブジェクトを保存することができる。リモートのソースからムービーデータを取ってきてアプリケーションに食わせることも可能だ。それにはGoogle App EngineのURL Fetch APIを使ってHTTPまたはHTTPS URLからデータを取得する。urlfetch.Fetch()メソッドを使えばリモートにあるイメージのバイナリコンテンツをフェッチできる。単に引数として画像のURLを渡してやるだけだ。

それからフェッチされた結果のcontent属性を引数として渡してBlobオブジェクトを作り、Movieオブジェクトのpicture属性に設定する。最後にput()メソッドを呼び出して変更をデータストアに保存する。

画像の取得と表示

これでオブジェクトがMovieデータストアに保存されたので、データストアから動的に取り出してブラウザに画像として表示しなおすリクエストハンドラを作成しよう。

初めにGETリクエストを処理するGetImageクラスを作成する。リクエストハンドラは最初に'title'の値をURLパラメータから抽出する。そしてgetMovie()メソッドを呼んでタイトルが一致するMovieオブジェクトをデータストアから読み込む。

Movieオブジェクトの参照が手に入れば、その"picture"属性にもアクセスできる。ブラウザで画像を適切にレンダリングするためにHTTP Content-Typeヘッダーは'image/jpg'に設定しておく。

class GetImage(webapp.RequestHandler):
  def get(self):
    title = self.request.get('title')
    movie = getMovie(title)
    if (movie and movie.picture):
      self.response.headers['Content-Type'] = 'image/jpg'
      self.response.out.write(movie.picture)
    else:
      self.redirect('/static/noimage.jpg')

getMoive()メソッドはタイトルの一致するムービーオブジェクトを取得する簡単なGQLからなる。

def getMovie(title):
  result = db.GqlQuery("SELECT * FROM Movie WHERE title = :1 LIMIT 1", 
                       title).fetch(1)
  if (len(result) > 0):
    return result[0]
  else:
    return None

アプリケーションでムービーの画像に簡単にアクセスできるようにGetImageクラスを'/image'というURLパスにマッチさせる。今回はGETリクエストを処理するのにwebappモジュールを利用することにした。

apps_binding = []
...
apps_binding.append(('/image', GetImage))
application = webapp.WSGIApplication(apps_binding, debug=True)
wsgiref.handlers.CGIHandler().run(application)

これでビジターがhttp://mydomain.com/image?title=matrixというURLにアクセスすると"matrix"ムービーの画像がブラウザに返される。