はじめに
フォントを軽量化するWebアプリを作った際に、サーバ側で3つのファイルをzipにしてダウンロードさせるということをしました。この時、サーバ側のストレージには保存できないのでファイルを保存せずに、データを送る必要があったので、そんなときの方法です。
Pythonでのストリーム
まず、ドキュメントはこちらです。
Pythonでストリームを扱うモジュールはio
です。特に、ファイルをストレージに保存せずにオンメモリで扱えるストリームには、文字列を扱うio.StringIO
と、バイナリを扱うio.BytesIO
の2種類があります。
ローカルで何かやる場合は、中間ファイルなども逐一保存して、デバッグなどに利用するかもしれませんが、本番環境で全て保存してたら、ストレージの容量がなくなってくので(特にフォントみたいに重めのデータや複数ファイルの場合)、オンメモリのストリームをうまく活用していきましょう。
実際に使ってみる
テキストモードファイルのように扱う
まず、StringIO
です。これを使うことで、普通の文字列を、open
で開いたテキストファイルのように扱うことができるようになります。
実際のコードがこちらです。
from io import StringIO
with StringIO() as bs:
bs.write('こんにちは世界\n')
bs.write('さよなら世界')
content = bs.getvalue()
print(type(content))
print(content)
これの出力結果はこちらです。
<class 'str'>
こんにちは世界
さよなら世界
ファイルストリームと同様にwrite
を使い、文字列を書き込んでいくことができます。そして最終的な文字列を得るためにはgetvalue
を使います。これにより得られるのは文字列(str
)です。
バイナリモードファイルのように扱う
次に、BytesIO
です。これを扱うと、様々なバイナリファイルをオンメモリで扱うことができるようになります。
実際のコードがこちらです。動作の確認のため、最後に結局ファイルとして保存するとかいう本末転倒なことしてますが、これぐらいしかみて取れる確認方法がなかったので、許してください。
from io import BytesIO
from PIL import Image
with BytesIO() as bs:
img = Image.new('L', (512, 512), 0)
img.save(bs, 'png')
content = bs.getvalue()
with open('img.png', mode='wb') as fs:
fs.write(content)
画像とかが見てわかりやすいかなと思ったので、画像を保存してみました。BytesIO
で作成したオブジェクトにPillowのsave
を使って、画像を保存します。ただ、この時点ではまだ、メモリ上に存在しています。
これを普通のファイルのように様々なライブラリ等に渡すこともできますが、今回は単純に保存するため、open
でwrite
します。
すると同じディレクトリにimg.png
という真っ黒な画像の出来上がりです。
おわりに
中間ファイルを保存せずに扱えるのは便利ですね。
zipにするのはまたちょっとややこしいので、別で書こうと思います。