Jef*_*son 14 javascript python stream flask
我有一个生成数据并实时流式传输的视图.我无法弄清楚如何将这些数据发送到我可以在我的HTML模板中使用的变量.我当前的解决方案只是在数据到达时将数据输出到空白页面,这有效,但我希望将其包含在带格式的较大页面中.如何在数据流式传输到页面时更新,格式化和显示数据?
import flask
import time, math
app = flask.Flask(__name__)
@app.route('/')
def index():
def inner():
# simulate a long process to watch
for i in range(500):
j = math.sqrt(i)
time.sleep(1)
# this value should be inserted into an HTML template
yield str(i) + '<br/>\n'
return flask.Response(inner(), mimetype='text/html')
app.run(debug=True)
Run Code Online (Sandbox Code Playgroud)
dav*_*ism 22
您可以在响应中流式传输数据,但不能按照描述的方式动态更新模板.模板在服务器端呈现一次,然后发送到客户端.您需要使用JavaScript来读取流式响应并在客户端输出数据.
用于XMLHttpRequest向将流式传输数据的端点发出请求.然后定期从流中读取,直到完成.
此示例假定一种非常简单的消息格式:单行数据,后跟换行符.只要有一种方法可以识别每条消息,您当然可以根据需要进行解析.例如,您可以返回JSON对象并在客户端上对其进行解码.
from time import sleep
from flask import Flask, render_template
from math import sqrt
app = Flask(__name__)
@app.route('/')
def index():
# render the template (below) that will use JavaScript to read the stream
return render_template('index.html')
@app.route('/stream_sqrt')
def stream():
def generate():
for i in range(500):
yield '{}\n'.format(sqrt(i))
sleep(1)
return app.response_class(generate(), mimetype='text/plain')
app.run()
Run Code Online (Sandbox Code Playgroud)
<p>This is the latest output: <span id="latest"></span></p>
<p>This is all the output:</p>
<ul id="output"></ul>
<script>
var latest = document.getElementById('latest');
var output = document.getElementById('output');
var xhr = new XMLHttpRequest();
xhr.open('GET', '{{ url_for('stream') }}');
xhr.send();
var position = 0;
function handleNewData() {
// the response text include the entire response so far
// split the messages, then take the messages that haven't been handled yet
// position tracks how many messages have been handled
// messages end with a newline, so split will always show one extra empty message at the end
var messages = xhr.responseText.split('\n');
messages.slice(position, -1).forEach(function(value) {
latest.textContent = value; // update the latest value in place
// build and append a new item to a list to log all output
var item = document.createElement('li');
item.textContent = value;
output.appendChild(item);
});
position = messages.length - 1;
}
var timer;
timer = setInterval(function() {
// check the response for new data
handleNewData();
// stop checking once the response has ended
if (xhr.readyState == XMLHttpRequest.DONE) {
clearInterval(timer);
latest.textContent = 'Done';
}
}, 1000);
</script>
Run Code Online (Sandbox Code Playgroud)
晚了 5 年,但这实际上可以按照您最初尝试的方式完成,javascript 是完全没有必要的(编辑:接受的答案的作者在我写完这篇文章后添加了 iframe 部分)。您只需要将输出嵌入为<iframe>:
from flask import Flask, render_template, Response
import time, math
app = Flask(__name__)
@app.route('/content') # render the content a url differnt from index
def content():
def inner():
# simulate a long process to watch
for i in range(500):
j = math.sqrt(i)
time.sleep(1)
# this value should be inserted into an HTML template
yield str(i) + '<br/>\n'
return Response(inner(), mimetype='text/html')
@app.route('/')
def index():
return render_template('index.html.jinja') # render a template at the index. The content will be embedded in this template
app.run(debug=True)
Run Code Online (Sandbox Code Playgroud)
然后,'index.html.jinja' 文件将包含一个<iframe>以内容 url 作为 src 的内容,类似于:
<!doctype html>
<head>
<title>Title</title>
</head>
<body>
<div>
<iframe frameborder="0" noresize="noresize"
style='background: transparent; width: 100%; height:100%;' src="{{ url_for('content')}}"></iframe>
</div>
</body>
Run Code Online (Sandbox Code Playgroud)
渲染render_template_string()时应使用用户提供的数据来渲染内容以避免注入攻击。但是,我将其排除在示例之外,因为它增加了额外的复杂性,超出了问题的范围,与 OP 无关,因为他没有流式传输用户提供的数据,并且与大量数据无关大多数人看到这篇文章,因为流式传输用户提供的数据是一种极端情况,很少有人需要这样做。
| 归档时间: |
|
| 查看次数: |
9154 次 |
| 最近记录: |