流式传输内容¶
有时,您希望发送非常巨量的数据到客户端,远远超过您可以保存在内存中的量。 在您实时地产生这些数据时,如何才能直接把他发送给客户端,而不需要在文件 系统中中转呢?
答案是使用发电机和直接反应。
基本用法¶
下面是一个简单的视图函数,这一视图函数实时生成大量的 CSV 数据, 这一技巧使用了一个内部函数,这一函数使用生成器来生成数据,并且 稍后激发这个生成器函数时,把返回值传递给一个 response 对象: 诀窍是有一个内部函数使用生成器生成数据,然后调用该函数并将其传递给响应对象:
from flask import Response
@app.route('/large.csv')
def generate_large_csv():
def generate():
for row in iter_all_rows():
yield ','.join(row) + '\n'
return Response(generate(), mimetype='text/csv')
每一个 yield
表达式直接被发送给浏览器。 现在,仍然有一些 WSGI 中间件可能 打断数据流,所以在这里请注意那些在带缓存快照的调试环境,以及其他一些您可能 激活了的东西。
从模板流式传输¶
Jinja2 模板引擎同样支持分块逐个渲染模板。 Flask没有直接暴露这个功能,因为它很不常见,但你可以自己轻松做:
from flask import Response
def stream_template(template_name, **context):
app.update_template_context(context)
t = app.jinja_env.get_template(template_name)
rv = t.stream(context)
rv.enable_buffering(5)
return rv
@app.route('/my-large-page.html')
def render_large_template():
rows = iter_all_rows()
return Response(stream_template('the_template.html', rows=rows))
这里的技巧是从应用程序上的Jinja2环境中获取模板对象,并调用stream()
而不是render()
,它返回一个流对象而不是字符串。 因为我们绕过了 Flask 的模板渲染函数,而是直接使用了模板对象,所以我们手动必须调用 update_template_context()
函数来确保更新了模板的渲染上下文。 然后在流被迭代时评估模板。由于每次执行yield时,服务器会将内容刷新到客户端,您可能希望缓存模板中的一些项目,您可以使用rv.enable_buffering(size)
来执行。5
是一个默认值。
使用上下文流式处理
版本0.9中的新功能。
注意,当你流数据,请求上下文已经走了函数执行的那一刻。Flask 0.9为您提供了一个帮助器,可以在生成器执行期间保持请求上下文:
from flask import stream_with_context, request, Response
@app.route('/stream')
def streamed_response():
def generate():
yield 'Hello '
yield request.args['name']
yield '!'
return Response(stream_with_context(generate()))
如果没有stream_with_context()
函数,您将在该点获得RuntimeError
。