モダントルネード:30行の分散イメージホスティング

竜巻は初めてですか? 聞いたことがありますが、非同期性を恐れていましたか? 6か月以上前に彼を見たことがありますか? 次に、この記事をあなたに捧げます。

準備する


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_idShowImageHandler.getimg_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のテンプレートの仕組みを理解したことです。 また、特にmongodbgridfsを非同期で使用する方法も知っています。

しかし...


おそらく1つのボトルネックに気づいたでしょう: file = self.request.files['file'][0] 。 はい、確かに、データベースに書き込む前に画像ファイル全体をメモリにロードします。 そして、おそらくNginxHttpUploadModuleのようなものを使用できると考えているでしょう。 ただし、tornado: tornado.web.stream_request_bodyを使用してこれを実行できるようになりました 。 おそらくこれは、次のレッスンのいずれかで行うことです。

リンク集




あなたの意見


気に入りましたか? 続行する必要がありますか? 訂正? お願い?

Source: https://habr.com/ru/post/J230607/


All Articles