準備する
3番目のpythonに書き込みます。 インストールされていない場合は、 pyenvを使用することをお勧めします。 竜巻に加えて、mongodbの非同期ドライバーであるモーターが必要です。
pip3 install tornado motor
必要なモジュールをインポートする
import bson import motor from tornado import web, gen, ioloop
gridfsに接続します
分散ストレージとして、 gridfsを使用します 。
db = motor.MotorClient().habr_tornado gridfs = motor.MotorGridFS(db)
最初の行では、 mongodbに接続し、ベース「habr_tornado」を選択します。 次に、gridfsに接続します(デフォルトではfsのコレクションになります)。
アップロードハンドラー
class UploadHandler(web.RequestHandler): @gen.coroutine def get(self): files = yield gridfs.find({}).sort("uploadDate", -1).to_list(20) self.render('upload.html', files=files) @gen.coroutine def post(self): file = self.request.files['file'][0] gridin = yield gridfs.new_file(content_type=file.content_type) yield gridin.write(file.body) yield gridin.close() self.redirect('')
tornado.web.RequestHandlerに従いました 。 そして、getメソッドとpostメソッドをオーバーライドして、対応するhttp要求のハンドラーを作成します。
tornado.gen.coroutineデコレーターを使用すると、非同期コールバックの代わりにジェネレーターを使用できます。
files = yield gridfs ...
、同期
files = gridfs
から回復するために視覚的にほとんどありません。 しかし、機能の違いは非常に大きいです。
yield
の場合、データベースへの非同期要求が発生し、データベースの完了を待機します。 つまり、データベースは「考える」一方で、サイトは他のリクエストを処理できます。
そのため、getメソッドでは、gridfsの最新ファイルからメタ情報を非同期的に取得します。 そして、それをテンプレートに向けます。
post
メソッドでは、送信された(テンプレートに描画されたフォームを使用して) 画像ファイルを取得します 。 次に、gridfsファイルを非同期で開き、そこに画像を保存して閉じます。 その後、同じページにリダイレクトして、更新されたファイルのリストを表示します。
ShowImageHandler
次に、gridfsから抜け出し、結果の画像を表示する必要があります。
class ShowImageHandler(web.RequestHandler): @gen.coroutine def get(self, img_id): try: gridout = yield gridfs.get(bson.objectid.ObjectId(img_id)) except (bson.errors.InvalidId, motor.gridfs.NoFile): raise web.HTTPError(404) self.set_header('Content-Type', gridout.content_type) self.set_header('Content-Length', gridout.length) yield gridout.stream_to_handler(self)
ここでは、GET HTTPリクエストのみを処理します。 まず、idによってgridfsから非同期的にファイルを取得します。 このIDは一意であり、画像がUploadHandlerに保存されたときに自動的に生成されました。 プロセスで例外が発生した場合(誤ったIDまたはファイルが見つからない)-404ページ目を表示します。 次に、ブラウザが応答を画像として識別するように適切なヘッダーを設定します。 そして、非同期で画像の本体を提供します。
ルーティング
ハンドラー(UploadHandlerおよびShowImageHandler)をURLにバインドするには、 tornado.web.Applicationのインスタンスを作成します。
app = web.Application([ web.url(r'/', UploadHandler), web.url(r'/imgs/([\w\d]+)', ShowImageHandler, name='show_image'), ])
パラメーターによって、URL正規表現のハンドラーへのマッピングを記述するリストを渡します。 通常のグループ
([\w\d]+)
は、
ShowImageHandler.get
として
img_id
に
ShowImageHandler.get
れ
img_id
。 そして、URLを生成するためにテンプレートで使用するパラメーター
name='show_image'
。
サーバーを起動します
app.listen(8000) ioloop.IOLoop.instance().start()
これで、ブラウザで結果を確認できます: http:// localhost:8000 /
模様
<!DOCTYPE html> <html> <h1>Upload an image</h1> <form action="" method="post" enctype="multipart/form-data"> <input type="file" name="file" accept="image/*" onchange="javascript:this.form.submit()"> </form> <h2>Recent uploads</h2> {% for file in files %} {% set url = reverse_url('show_image', file['_id']) %} <a href="{{ url }}"><img src="{{ url }}" style="max-width: 50px;"></a> {% end %} </html>
ここではすべて、djangoまたはjinjaでおなじみのはずです。 唯一の違い:
endfor
代わりに
endfor
結果
そのため、高速でスケーラブルな非同期の性質を備えていますが、擬似同期スタイルの画像ホスティングで記述されています。 そして最も重要なことは、ルーティング、リクエストハンドラ、およびtornadoのテンプレートの仕組みを理解したことです。 また、特にmongodbとgridfsを非同期で使用する方法も知っています。
しかし...
おそらく1つのボトルネックに気づいたでしょう:
file = self.request.files['file'][0]
。 はい、確かに、データベースに書き込む前に画像ファイル全体をメモリにロードします。 そして、おそらくNginxHttpUploadModuleのようなものを使用できると考えているでしょう。 ただし、tornado: tornado.web.stream_request_bodyを使用してこれを実行できるようになりました 。 おそらくこれは、次のレッスンのいずれかで行うことです。
リンク集
あなたの意見
気に入りましたか? 続行する必要がありますか? 訂正? お願い?