Streaming Contents

Sometimes you want to send an enormous amount of data to the client, muchmore than you want to keep in memory. When you are generating the data onthe fly though, how do you send that back to the client without theroundtrip to the filesystem?

The answer is by using generators and direct responses.

Basic Usage

This is a basic view function that generates a lot of CSV data on the fly.The trick is to have an inner function that uses a generator to generatedata and to then invoke that function and pass it to a response object:

  1. from flask import Response
  2.  
  3. @app.route('/large.csv')
  4. def generate_large_csv():
  5. def generate():
  6. for row in iter_all_rows():
  7. yield ','.join(row) + '\n'
  8. return Response(generate(), mimetype='text/csv')

Each yield expression is directly sent to the browser. Note thoughthat some WSGI middlewares might break streaming, so be careful there indebug environments with profilers and other things you might have enabled.

Streaming from Templates

The Jinja2 template engine also supports rendering templates piece bypiece. This functionality is not directly exposed by Flask because it isquite uncommon, but you can easily do it yourself:

  1. from flask import Response
  2.  
  3. def stream_template(template_name, **context):
  4. app.update_template_context(context)
  5. t = app.jinja_env.get_template(template_name)
  6. rv = t.stream(context)
  7. rv.enable_buffering(5)
  8. return rv
  9.  
  10. @app.route('/my-large-page.html')
  11. def render_large_template():
  12. rows = iter_all_rows()
  13. return Response(stream_template('the_template.html', rows=rows))

The trick here is to get the template object from the Jinja2 environmenton the application and to call stream() instead ofrender() which returns a stream object instead of astring. Since we’re bypassing the Flask template render functions andusing the template object itself we have to make sure to update the rendercontext ourselves by calling update_template_context().The template is then evaluated as the stream is iterated over. Since eachtime you do a yield the server will flush the content to the client youmight want to buffer up a few items in the template which you can do withrv.enable_buffering(size). 5 is a sane default.

Streaming with Context

Changelog

New in version 0.9.

Note that when you stream data, the request context is already gone themoment the function executes. Flask 0.9 provides you with a helper thatcan keep the request context around during the execution of thegenerator:

  1. from flask import stream_with_context, request, Response
  2.  
  3. @app.route('/stream')
  4. def streamed_response():
  5. def generate():
  6. yield 'Hello '
  7. yield request.args['name']
  8. yield '!'
  9. return Response(stream_with_context(generate()))

Without the stream_with_context() function you would get aRuntimeError at that point.