有时,您希望发送非常巨量的数据到客户端,远远超过您可以保存在内存中的量。 在您实时地产生这些数据时,如何才能直接把他发送给客户端,而不需要在文件 系统中中转呢?
答案是生成器和 Direct Response。
下面是一个简单的视图函数,这一视图函数实时生成大量的 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) 来实现,size 的较为合理的默认值是 5 。