ラベル ファイルアップロード の投稿を表示しています。 すべての投稿を表示
ラベル ファイルアップロード の投稿を表示しています。 すべての投稿を表示

2007年11月11日

FormEncodeでイメージアップロード用のバリデータを作る

FormEncode



さて、FormBuildにしろ、ToscaWidgets(twForm) にしろ、入力チェックにはFormEncodeを使っています。
FormEncodeは、文字列からpythonの値(intや、datetimeなど)に変換と入力チェックを同時にやってくれます。
入力チェックに通った文字列は安全に変換できるってことですね。



標準で、多くのバリデータを含んでいますが、拡張するのも簡単にできるようになっています。
FancyValidatorという、カスタムバリデータを作るための基底クラスも用意されています。
既存のバリデータを継承して、チェック内容を増やすのも簡単です。



ファイルアップロードのバリデータに、FieldStorageUploadConverterがすでにあります。
このバリデータを拡張して、イメージファイル専用のバリデータを作ってみました。



Python Imaging Library



PILと略されて呼ばれることもあります。
Pythonで画像処理をするときに、一番有名なモジュールと思われます。
標準ライブラリに入って欲しいくらいなのだけど、ライセンスの関係からか含まれる予定はありません。
フリーで使えるのは、最新版よりも1つ古いバージョンのものになります。




例えばサムネイルを作って保存するには以下のようになります。

import Image
im = Image.open(filename)
im.thumbnail(size, PIL.Image.ANTIALIAS)
im.save(thumbfilename)

ファイルから、イメージオブジェクトを作って、thumbnailメソッドで変換。
saveメソッドで保存。
というとてもわかりやすいインターフェイスです。



イメージファイルアップロード用のバリデータ



ファイルアップロードされる内容は色々ありますが、イメージファイルのみをアップロード対象とする場合も結構多いと思います。
こういう場合には、ファイルライクオブジェクトが返ってくるより、イメージオブジェクトが返ってくる方が自然に思えます。




FieldStorageUploadConverterを継承して、さらに、イメージに変換する処理を付け加えました。

from formencode import Invalid
from formencode.validators import *
import Image
import os

class ImageFile(FieldStorageUploadConverter):

def _to_python(self, value, state=None):
imagefile = FieldStorageUploadConverter._to_python(self, value, state)
try:
image = Image.open(imagefile.file)
if self.height is not None and image.size[1] > self.height:
raise Invalid('Please image height make less than %d.' % self.height,
value, state)
if self.width is not None and image.size[0] > self.width:
raise Invalid('Please image width make less than %d.' % self.width,
value, state)

return image
except IOError, e:
raise Invalid('Invalid format.', value, state)

これを使うと、Pylonsでrequest.params['image'] などに、Imageオブジェクトが格納されます。
また、ファイルの内容が画像でない(PILで処理できないフォーマット)の場合は、Invalid Format というエラーメッセージが設定されます。
また、バリデータをインスタンス化する際に、コンストラクタに、widthやheightを指定すると、アップロードされた画像のサイズをチェックして、サイズ内に収まっていない場合にバリデーションエラーにします。


参考



追記


サイズチェックを追加しました。

2007年10月22日

Pylons RestController でのファイルアップロード


Pylonsはpasterコマンドで、コントローラークラスの元になるコードを生成してくれます。
コントローラークラスは二種類あって、普通のコントローラーとRestControllerです。
RestControllerは、リソースのCRUD処理を主に行う場合に使用します。



このRestControllerを使って、コメント付画像アップロードを試してたのですが、ちょっとはまりどころがありました。
PylonsはURLディスパッチャにRoutesを使っています。
RestControllerでは、GET, POST, PUT, DELETE といったMETHODに応じて、呼び出すメソッドが変わりますが、実際のHTMLではGETとPOSTしか使えません。
Routesは、この部分を_methodという隠しパラメータを使って疑似的に、PUTやDELETEを取り扱っています。
実際のメソッドは、POSTだけど、_methodにPUTを設定しておくと、Routes内では、PUTとして取り扱うようになっています。



しかし、現在リリースされているRoutesのmiddlewareは、enctypeがapplication/x-www-form-urlencoded(要するにデフォルト)の場合しか、_methodを見てくれません。
ファイルアップロードするときは、enctypeをmultipart/form-dataとするわけで、RestControllerのupdateメソッドをどうしても読んでくれませんでした。



ソースを見ると結局、以下のようになっているわけで、どうやっても無理です。



elif environ['REQUEST_METHOD'] == 'POST' and \
'application/x-www-form-urlencoded' in environ.get('CONTENT_TYPE',
'') \
and'_method' in req.POST:


強引に、multipart/form-dataをねじ込んでみました。



elif environ['REQUEST_METHOD'] == 'POST' and \
('application/x-www-form-urlencoded' in environ.get('CONTENT_TYPE',
'') \
or 'multipart/form-data' in environ.get('CONTENT_TYPE',
'')) \
and'_method' in req.POST:


まあ、動きました。



ちなみにこの問題は、10/22に取得したsvnでは、しっかり直ってました。
早くリリースして欲しいぞ。



必要な人がいるかどうか分かりませんが、上記の修正分diff



diff -u routes.orig/middleware.py routes/middleware.py
--- routes.orig/middleware.py 2007-10-22 22:59:17.000000000 +0900
+++ routes/middleware.py 2007-10-22 23:03:43.000000000 +0900
@@ -56,8 +56,10 @@
log.debug("_method found in QUERY_STRING, altering request"
" method to %s", environ['REQUEST_METHOD'])
elif environ['REQUEST_METHOD'] == 'POST' and \
- 'application/x-www-form-urlencoded' in environ.get('CONTENT_TYPE',
+ ('application/x-www-form-urlencoded' in environ.get('CONTENT_TYPE',
'') \
+ or 'multipart/form-data' in environ.get('CONTENT_TYPE',
+ '')) \
and'_method' in req.POST:
old_method = environ['REQUEST_METHOD']
environ['REQUEST_METHOD'] = req.POST['_method'].upper()

参考