使用 Python 和 Flask 流式传输数据
Posted
技术标签:
【中文标题】使用 Python 和 Flask 流式传输数据【英文标题】:Streaming data with Python and Flask 【发布时间】:2012-11-03 09:54:41 【问题描述】:我似乎无法弄清楚如何使用 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>
我希望在页面上看到每半秒更改一次的计数器。相反,我得到的最接近的是在下一行打印出每个数字的页面。
【问题讨论】:
【参考方案1】:我认为如果你要使用这样的模板,你可能需要使用这里给出的stream_template
函数:http://flask.pocoo.org/docs/patterns/streaming/#streaming-from-templates
我没有对此进行测试,但它可能看起来像:
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('/scans/')
def scans_query():
url_for('static', filename='.*')
def generate():
for i in xrange(50):
sleep(.5)
yield i
return Response(stream_template('scans.html', i=generate()))
【讨论】:
使用此解决方案浏览localhoast:5000/scans
在我的计算机上出现以下错误:jinja2.exceptions.TemplateSyntaxError: Encountered unknown tag 'i'.
有关如何成功运行它的任何建议?
@SpeedCoder5 这段代码是在 2012 年写的。我怀疑最近的 Flask 版本和 Python 版本需要不同的解决方案或至少有语法更改。【参考方案2】:
要替换页面上的现有内容,您可能需要 javascript,即,您可以发送它或让它为您发出请求,使用长轮询、websockets 等。有很多方法可以做到这一点,这里有一个使用 @ 987654321@:
#!/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
:
<!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()
。
注意:如果流不是无限的,则使用其他技术(不是 SSE),例如,发送 javascript sn-ps 来替换文本(无限 <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
:
<!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 %
【讨论】:
您提供的演示(特别是我正在玩 SSE 演示)仅适用于单个客户端。如果我打开一个新的浏览器窗口并尝试访问页面流数据,则在我关闭或停止上一页之前什么都不会发生。然后,计数器从 0 开始重新启动。您将如何修改它,以便任何尝试访问它的客户端都可以看到相同的数据/计数器,从应用程序启动的时间开始计数?我假设您必须在单独的线程中运行计数器,但我不确定如何实现。 @DavidMarx:至少有两个问题:(1)flask中如何支持多个并发客户端? — 答案:与任何 wsgi 应用程序一样,例如,使用 gunicorn (2) 如何为多个客户端提供对同一个柜台的访问权限? — 就像您在任何服务器程序中提供对共享数据的访问一样,例如,假设一个工作人员:定义全局迭代器并在循环中调用next(it)
。无论如何,这些都是单独的问题。针对您的特定问题提出一个新问题。
尝试第一个示例读取日志文件并在 html 中显示,但 html 仅打印第一行.. 但 print() 标有 **** 打印所有行 def events(): with open('E :/Study/loggerApp/templates/job.log') as f: data=""; while True: data=f.read() yield "data: "+(data) if data!="": ****print(data)**** time.sleep(1) return Response(events() , content_type='文本/事件流')
@Karan:我很惊讶代码在 8 年后仍然有效。要发送多行数据,您可以尝试将每一行作为单独的事件发送,或者仅将数据作为 json 发送(在这种情况下,换行符被转义)。以上是关于使用 Python 和 Flask 流式传输数据的主要内容,如果未能解决你的问题,请参考以下文章
当使用flask进行流式传输时,只有opencv的VIDEO错误。