Python3 + Bottle + Pillow でレスポンスとして画像を返す
とりあえず結論
Python3 で画像を取り扱うには、Pillow(PILの派生版, Python3対応)を使うと良いようです。
サーバー側で作成・処理した Pillow のオブジェクトを、HTTPレスポンスで画像としてクライアントに表示させるには、次のようにします。 ポイントは、Pillow オブジェクトの保存先に BytesIO オブジェクトを指定している点です。
from bottle import default_app, route, HTTPResponse from io import BytesIO from PIL import Image import requests application = default_app() @route('/route/to/your/image/shown') def show_image(): # URLから画像を開く image_url = "path/to/target/image" response = requests.get(image_url) pillow_object = Image.Open(BytesIO(response.content)) # -- ここで pillow_object に対して何らかの処理 -- # HTTPレスポンスを作成する output = BytesIO() pillow_object.save(output, format='png') res = HTTPResponse(status=200, body=output.getvalue()) res.set_header('Content-Type', 'image/png') return res
サーバーでは wsgi 対応フレームワークとしてBottle を使用しています。
クライアント側では、以下のようにして呼び出すことができます。
<img src="/route/to/your/image/shown"/>
Python3 と StringIO, BytesIO
ところで、ネットで上記の方法を調べようとして適当なキーワードで検索すると、以下のような回答にぶつかります。
See Image.save(). It can take a file object in which case you can write it to a StringIO instance. Thus something like:
output = StringIO.StringIO() base.save(output, format='PNG') return [output.getvalue()]
python imaging library - How to return in-memory PIL image from WSGI application - Stack Overflow
画像イメージを StringIO に保存すると良い、ということですね。ところが、これは Python 3.x ではうまくいきません。なぜかというと、
The StringIO and cStringIO modules are gone. Instead, import the io module and use io.StringIO or io.BytesIO for text and data respectively.
python - How to use StringIO in Python3? - Stack Overflow
What’s New In Python 3.0 — Python v3.0.1 documentation
つまり、Python 2 で有効であった StingIO や cStyingIO は、Python 3 では撤廃されて、代わりに io.StringIO と io.BytesIO になったことが原因です。 そこで 上記のStackOverflow の回答のように、
try: from StringIO import StringIO except ImportError: from io import StringIO
StringIO.StringIO の代わりに io.StringIO で処理しようとしても、今回のケースではやはりうまく行きません。
公式サイトのドキュメントを読むとわかるのですが、画像などバイナリファイルを扱うには、bytes オブジェクトに対応した io.BytesIO でなければいけないのですね。 StringIO.StringIO と io.StringIO の名前が似ているのでややこしいのですが、Python3 ではより適切に役割が分離されたということでしょう。
テキスト I/O は、 str オブジェクトを受け取り、生成します。すなわち、背後にあるストレージがバイト列 (例えばファイルなど) を格納するときは常に、透過的にデータのエンコード・デコードを行ない、オプションでプラットフォーム依存の改行文字変換を行います。
バイナリー I/O (buffered I/O とも呼ばれます) は bytes オブジェクトを受け取り、生成します。エンコード、デコード、改行文字変換は一切行いません。このカテゴリのストリームは全ての非テキストデータや、テキストデータの扱いを手動で管理したい場合に利用することができます。
io --- ストリームを扱うコアツール — Python 3.10.0b2 ドキュメント
MongoDB に画像を保存するときに BytesIO を使用するのも同様の理由と思われます。