djangoappengineでBlobstoreを用いてファイルアップロード・ダウンロードを実装する

djangoappengineでファイルアップロード、ダウンロードを実装するにはfiletransferを使うのが基本だと思いますが、このサンプル通りに実装すると、アップロード時の動きがGoogle App Engineに隠蔽されまくってて、謎の挙動がいっぱいあります。(単純に私のスキルに起因するものかと思いますが)

そこで、ちょっと工夫してみたいと思います。

モデルにフィールドを追加する

ファイルを保持させたいモデルにフィールド追加します。例えば、

class UserProfile(models.Model)
    profile_photo = models.FileField(upload_to='p', blank=True)

といったように。upload_toは適当で良いと思いますが、指定しておかないとどっかでエラーしたような気がします。

アップロードを実装

viewのファイルを編集します。アップロードされたファイルはrequest.FILES['profile_photo']といった形で取得できます。
このファイルを以下の関数でブロブストアに保存します。
(※2011/06/20現在、この方法はExperimental(実験的)とされています。使うかどうかは各個人の責任としてください)

from __future__ import with_statement #ファイルの先頭に記述する必要あり
from google.appengine.api import files

def _upload_to_blobstore(file):
    file_name = files.blobstore.create(mime_type='image/jpeg')
    with files.open(file_name, 'a') as f:
        for chunk in file.chunks():
            f.write(chunk)
    files.finalize(file_name)
    return files.blobstore.get_blob_key(file_name)

戻り値でBlob Keyが返ってきます。これはダウンロードの際に必要になるので、データベースに保存します。

bk = _upload_to_blobstore(request.FILES['profile_photo'])
# upがUserProfileのインスタンスだとすると
up.profile_photo = str(bk)

up.profile_photoはFileFieldですが、内部的にはBlobKeyが文字列で保存されているだけなので、問題なく動作します。

ダウンロードを実装

ここもAppEngineのAPIを直接たたこうかと思いましたが、あまり上手くいかなかったので、filetransferを使います。
ダウンロードに関してはサンプル通りに実装すれば問題ありません。

def profile_photo_download_handler(request, user_pk):
    profile = get_object_or_404(UserProfile, user__pk=user_pk)
    if profile.profile_photo:
        return serve_file(request, profile.profile_photo)
    else:
        return HttpResponseRedirect('/static/images/default_profile_photo.png')

こんな感じで。
適当に解説してきましたが、不明な点、質問がありましたら、コメントしてください。答えられる範囲で答えます。