Python BytesIOとは?メモリ上でバイナリを扱う方法
io.BytesIOは、メモリ上でバイナリファイルのように読み書きできるオブジェクトです。
画像やPDFをディスクに保存せずに処理できます。
BytesIOの基本
import io
b = io.BytesIO()
b.write(b"\x00\x01binary")
print(b.getvalue())
b.seek(0)
print(b.read(3))
実行結果:
b'\x00\x01binary'
b'\x00\x01b'
扱うのはbytesです。文字列は書き込めません。
io.BytesIO().write("abc")
実行結果:
TypeError: a bytes-like object is required, not 'str'
StringIOとの違い
StringIO |
BytesIO |
|
|---|---|---|
| 扱う型 | str |
bytes |
| 対応するモード | "r" / "w" |
"rb" / "wb" |
| 用途 | テキスト・CSV・JSON | 画像・PDF・zip |
実際のファイルで"rb"を使う場面ならBytesIOと覚えてください。
ダウンロードした画像を処理する
import io
import urllib.request
from PIL import Image
with urllib.request.urlopen(url, timeout=10) as r:
data = r.read()
img = Image.open(io.BytesIO(data))
print(img.size)
これが最も多い使い方です。
Image.open()はファイルオブジェクトを受け取りますが、受信したデータはbytesです。BytesIOで包むとファイルとして渡せます。
一時ファイルを作る必要がありません。
- ディスクに書かないので速い
- 後片付けが不要
- 書き込み権限がない環境でも動く
画像を保存せずに返す
import io
from PIL import Image
img = Image.new("RGB", (100, 100), "red")
buf = io.BytesIO()
img.save(buf, format="PNG")
data = buf.getvalue()
print(len(data), "bytes")
print(data[:8])
PNGのバイト列がメモリ上に作られます。
Webアプリで画像を生成して返すとき、この形でレスポンスに載せます。
buf.seek(0)
return send_file(buf, mimetype="image/png")
seek(0)を忘れると空のファイルが返ります。save()のあと位置が末尾にあるためです。
これは非常によくある不具合です。「画像が壊れている」「サイズが0」という症状が出たら、まずここを確認してください。
zipをメモリ上で作る
import io
import zipfile
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w") as z:
z.writestr("a.txt", "1つ目のファイル")
z.writestr("b.txt", "2つ目のファイル")
print(len(buf.getvalue()), "bytes")
buf.seek(0)
with zipfile.ZipFile(buf) as z:
print(z.namelist())
print(z.read("a.txt").decode("utf-8"))
実行結果:
238 bytes
['a.txt', 'b.txt']
1つ目のファイル
ファイルを1つも作らずにzipを組み立てられました。
複数ファイルをまとめてダウンロードさせる機能などに使えます。
bytesとStringIOの変換
import io
data = "日本語テキスト".encode("utf-8")
text = io.BytesIO(data).read().decode("utf-8")
print(text)
実行結果:
日本語テキスト
テキストとして扱いたいだけなら、先にdecode()してStringIOに入れるほうが素直です。
s = io.StringIO(data.decode("utf-8"))
もう1つの方法として、TextIOWrapperで包むこともできます。
wrapper = io.TextIOWrapper(io.BytesIO(data), encoding="utf-8")
print(wrapper.read())
バイナリのストリームをテキストとして読むときに使います。巨大なデータを一度にdecode()したくない場合に有効です。
メモリを使う点に注意
BytesIOは全体をメモリに保持します。
数百MBのファイルを扱うと、そのぶんメモリを消費します。
| データの大きさ | 選ぶもの |
|---|---|
| 数MB程度 | BytesIO |
| 大きい・不定 | tempfile |
| 順に処理できる | ストリーム処理 |
大きなファイルは一時ファイルに書き出してください。「Python TemporaryFileとは?名前を持たない一時ファイルを使う方法」を参照してください。
BytesIOのまとめ
- メモリ上でバイナリをファイルのように扱う
- 扱うのは
bytes——文字列は不可 - ダウンロードした画像を保存せず処理できる
- 書き込み後は
seek(0)——忘れると空になる - zipもメモリ上で組み立てられる
- 全体がメモリに載る——大きなデータには不向き
あわせて読みたい
- Python StringIOとは?文字列をファイルのように扱う方法
- Python TemporaryFileとは?名前を持たない一時ファイルを使う方法
- Python urlopen()とは?標準ライブラリでHTTP通信する方法
- Python base64とは?バイナリを文字列に変換する方法を解説
- Python pickleとは?オブジェクトを保存・復元する方法を解説
- Python「open()」の使い方|ファイルを読み書きする基本関数を初心者向けに解説
- Python structとは?バイナリデータを変換する方法
- Python waveとは?WAVファイルを読み書きする方法
- Python ftplibとは?FTPでファイルを転送する方法





