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



しかし...



おそらく1つのボトルネックに気づいたでしょう: file = self.request.files['file'][0]



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



リンク集







あなたの意見



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



All Articles