流处理#
请求流处理#
Sanic 允许客户端发送的数据字节到达时开始处理数据。
在端点上启用时,你可以使用 await request.stream.read()
来流式处理请求体。
from sanic.views import stream, HTTPMethodView
class SimpleView(HTTPMethodView):
@stream
async def post(self, request):
result = ""
while True:
body = await request.stream.read()
if body is None:
break
result += body.decode("utf-8")
return text(result)
当请求体完成时,该方法将返回 None
。
它还可以通过装饰器中的关键字参数启用:
@app.post("/stream", stream=True)
async def handler(request):
...
body = await request.stream.read()
...
或者使用 add_route()
:
bp.add_route(
bp_handler,
"/bp_stream",
methods=["POST"],
stream=True,
)
响应流处理#
Sanic 允许你将内容流式传输到客户端。
@app.route("/")
async def test(request):
response = await request.respond(content_type="text/csv")
await response.send("foo,")
await response.send("bar")
# Optionally, you can explicitly end the stream by calling:
await response.eof()
这在你想要将源自外部服务(如数据库)的内容流式传输到客户端的情况下非常有用。例如,你可以使用 asyncpg
提供的异步游标将数据库记录流式传输到客户端。
@app.route("/")
async def index(request):
response = await request.respond()
conn = await asyncpg.connect(database='test')
async with conn.transaction():
async for record in conn.cursor('SELECT generate_series(0, 10)'):
await response.send(record[0])
你可以通过调用 await response.eof()
来明确结束流。这是一个方便的方法,用来替换 await response.send("", True)
。在你的处理程序确定没有其他内容要发送回客户端后,应该调用一次。虽然在 Sanic 服务器中使用它是可选的,但如果你在 ASGI 模式下运行 Sanic,那么你必须明确终止流。
文件流#
Sanic 提供了 sanic.response.file_stream
函数,当你想发送大文件时非常有用。它返回 StreamingHTTPResponse
对象,并默认使用分块传输编码;因此,Sanic 不会在响应中添加 Content-Length
HTTP 头。
典型的用例可能是流式传输视频文件。
@app.route("/mp4")
async def handler_file_stream(request):
return await response.file_stream(
"/path/to/sample.mp4",
chunk_size=1024,
mime_type="application/metalink4+xml",
headers={
"Content-Disposition": 'Attachment; filename="nicer_name.meta4"',
"Content-Type": "application/metalink4+xml",
},
)
如果你想使用 Content-Length
头,你可以通过添加 Content-Length
头来禁用分块传输编码并手动添加。
from aiofiles import os as async_os
from sanic.response import file_stream
@app.route("/")
async def index(request):
file_path = "/srv/www/whatever.png"
file_stat = await async_os.stat(file_path)
headers = {"Content-Length": str(file_stat.st_size)}
return await file_stream(
file_path,
headers=headers,
)