我似乎无法弄清楚如何使用Flask的流式传输。这是我的代码:
@app.route('/scans/') def scans_query(): url_for('static', filename='.*') def generate(): yield render_template('scans.html') for i in xrange(50): sleep(.5) yield render_template('scans.html', **locals()) return Response(stream_with_context(generate()))
在我的模板中:
<p>{% i %}</p>
我希望页面上的计数器每半秒更改一次。相反,我得到的最接近的页面是在下一行打印出每个数字。
要替换页面上的现有内容,您可能需要JavaScript,例如,可以发送它或使其为您发出请求,使用长时间轮询,websocket等。有许多方法可以使用,这是一种使用服务器发送事件的方法:
#!/usr/bin/env python import itertools import time from flask import Flask, Response, redirect, request, url_for app = Flask(__name__) @app.route('/') def index(): if request.headers.get('accept') == 'text/event-stream': def events(): for i, c in enumerate(itertools.cycle('\|/-')): yield "data: %s %d\n\n" % (c, i) time.sleep(.1) # an artificial delay return Response(events(), content_type='text/event-stream') return redirect(url_for('static', filename='index.html')) if __name__ == "__main__": app.run(host='localhost', port=23423)
哪里static/index.html:
static/index.html
<!doctype html> <title>Server Send Events Demo</title> <style> #data { text-align: center; } </style> <script src="http://code.jquery.com/jquery-latest.js"></script> <script> if (!!window.EventSource) { var source = new EventSource('/'); source.onmessage = function(e) { $("#data").text(e.data); } } </script> <div id="data">nothing received yet</div>
如果连接断开,浏览器默认会在3秒内重新连接。如果没有其他要发送的内容,则服务器可以返回404或仅发送'text/event- stream'内容类型以外的其他内容来响应下一个请求。即使服务器上有更多数据,也要在客户端停止,可以致电source.close()。
'text/event- stream'
source.close()
注意:如果流不是无限的,则使用其他技术(而不是SSE),例如,发送javascript代码段替换文本(无限<iframe>技术):
<iframe>
#!/usr/bin/env python import time from flask import Flask, Response app = Flask(__name__) @app.route('/') def index(): def g(): yield """<!doctype html> <title>Send javascript snippets demo</title> <style> #data { text-align: center; } </style> <script src="http://code.jquery.com/jquery-latest.js"></script> <div id="data">nothing received yet</div> """ for i, c in enumerate("hello"): yield """ <script> $("#data").text("{i} {c}") </script> """.format(i=i, c=c) time.sleep(1) # an artificial delay return Response(g()) if __name__ == "__main__": app.run(host='localhost', port=23423)
我已在此处内联html以显示没有更多内容(没有魔术)。与上面相同,但使用模板:
#!/usr/bin/env python import time from flask import Flask, Response app = Flask(__name__) def stream_template(template_name, **context): # http://flask.pocoo.org/docs/patterns/streaming/#streaming-from-templates app.update_template_context(context) t = app.jinja_env.get_template(template_name) rv = t.stream(context) # uncomment if you don't need immediate reaction ##rv.enable_buffering(5) return rv @app.route('/') def index(): def g(): for i, c in enumerate("hello"*10): time.sleep(.1) # an artificial delay yield i, c return Response(stream_template('index.html', data=g())) if __name__ == "__main__": app.run(host='localhost', port=23423)
哪里templates/index.html:
templates/index.html
<!doctype html> <title>Send javascript with template demo</title> <style> #data { text-align: center; } </style> <script src="http://code.jquery.com/jquery-latest.js"></script> <div id="data">nothing received yet</div> {% for i, c in data: %} <script> $("#data").text("{{ i }} {{ c }}") </script> {% endfor %}