タイトルの通りです。Python の FastAPI で簡単なサンプルアプリを作成しました。
実行例
FastAPI のコード
HTTP は昔からストリームなので HTTP レスポンスのボディを逐次、送受信することは以前から可能でした。でも HTTPヘッダの Content-Length
がネックでした。HTTPヘッダは最初に返さないといけないのでその時点でコンテンツサイズが未定の場合は Content-Length
を設定できないからです。
HTTP1.1 が全盛の頃はコンテンツを細切れ(チャンク)にして送信していましたが今は、楽に実装できるようになりました。FastAPI は、StreamingResponse
を使います。ブラウザ側は、HTTPレスポンスの ReadableStream
を getReader()
して Reader
を取得します。
main.py
import asyncio
from fastapi import FastAPI
from fastapi.responses import HTMLResponse, StreamingResponse
app = FastAPI()
# HTTPストリーミングレスポンスを返す
@app.get("/streaming")
async def streaming():
text = '''吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当けんとうがつかぬ。
何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。
吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪どうあくな種族であったそうだ。
この書生というのは時々我々を捕つかまえて煮にて食うという話である。
'''
async def generate_data():
for c in text:
yield c
await asyncio.sleep(0.05)
return StreamingResponse(generate_data(), media_type="text/plain")
# HTMLを返す
@app.get("/")
async def index():
html = '''
<html>
<body>
<h1>Hello Streaming!</h1>
<div id="foo" style="white-space: pre-wrap;"></div>
<script>
document.addEventListener('DOMContentLoaded', async function () {
const div = document.getElementById("foo");
const response = await fetch("/streaming", {method: "GET"});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const decoder = new TextDecoder();
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
div.textContent = div.textContent + text;
}
});
</script>
</body>
</html>
'''
return HTMLResponse(content=html)
実行方法
フォルダ構成
./
├── docker-compose.yml
├── Dockerfile
├── requirements.txt
└── app/
└── main.py
Dockerfile
Dockerfile
FROM public.ecr.aws/docker/library/python:3.12-slim
WORKDIR /app
COPY ./requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY app/. ./
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
docker-compose.yml
docker-compose.yml
services:
streaming:
image: streaming:0.1
build:
context: .
dockerfile: ./Dockerfile
container_name: streaming
working_dir: /app
ports:
- "0.0.0.0:8000:8000"
volumes:
- ./app:/app
requirements.txt
requirements.txt
fastapi
uvicorn[standard]
実行
docker compose build
docker compose up -d
ブラウザで http://localhost:8000/
を開く。
以上です.