流内容¶
有时候你想要向用户端发送特别巨量的数据,远比你想在内存中存的多。在当你动态生成数据时,如何将其发送回用户端而无需让数据在文件系统中往返?
答案是使用生成器(generators)和直接响应(direct responses)。
基本用法¶
下面是一个基本的视图函数,它动态生成大量的 CSV 数据。关键是在内部定义一个使用生成器生成数据的函数,然后将该函数传递给响应对象:
@app.route('/large.csv')
def generate_large_csv():
def generate():
for row in iter_all_rows():
yield f"{','.join(row)}\n"
return generate(), {"Content-Type": "text/csv"}
每个 yield 表达式会被直接发送到浏览器。但请注意,有些 WSGI 中间件可能会破坏流式传输,因此在启用调试环境中的分析器(profilers)或其他工具时要小心。
基于模板发送流内容¶
Jinja2 模板引擎支持按片段渲染模板,返回一个字符串迭代器。Flask 提供了 stream_template() 和 stream_template_string() 函数,使得这种用法更加方便。
from flask import stream_template
@app.get("/timeline")
def timeline():
return stream_template("timeline.html")
渲染流中产生的部分通常与模板中的语句块相对应。
基于上下文的流传输¶
当生成器正在运行时,request 将不再处于活动状态,因为此时视图函数已经返回。如果你尝试访问 request,将会得到一个 RuntimeError。
如果你的生成器函数依赖于 request 中的数据,请使用 stream_with_context() 包装器。它会在生成器运行期间保持请求上下文为活动状态。
from flask import stream_with_context, request
from markupsafe import escape
@app.route('/stream')
def streamed_response():
def generate():
yield '<p>Hello '
yield escape(request.args['name'])
yield '!</p>'
return stream_with_context(generate())
该函数也可以用作装饰器。
@stream_with_context
def generate():
...
return generate()
当有请求处于活动状态时,stream_template() 和 stream_template_string() 函数会自动使用 stream_with_context()。