如何将文本数据流式传输(产生)到Pythonywhere上的flask应用程序的jinja2模板

如何将文本数据流式传输(产生)到Pythonywhere上的flask应用程序的jinja2模板,flask,wsgi,pythonanywhere,Flask,Wsgi,Pythonanywhere,我尝试在我的Pythonanywhere帐户上实现“流式内容” 它或多或少地与上面显示的内容类似: 比照 除此之外,我的视图正在计算一个复杂的过程,可能需要一分钟的时间,并将其数据生成到我的模板,其中脚本应该更新一些进度条(“source.onmessage”) 这在我的开发机器上非常有效,但在我的pythonywhere帐户上却不行。在这台服务器上,进程看起来很拥挤(进度条永远不会更新,除非在最后,进度从0%增长到100%),尽管一切都进展顺利,例如我的print语句正确地呈现在我的服务器日志

我尝试在我的Pythonanywhere帐户上实现“流式内容”

它或多或少地与上面显示的内容类似: 比照

除此之外,我的视图正在计算一个复杂的过程,可能需要一分钟的时间,并将其数据生成到我的模板,其中脚本应该更新一些进度条(“source.onmessage”)

这在我的开发机器上非常有效,但在我的pythonywhere帐户上却不行。在这台服务器上,进程看起来很拥挤(进度条永远不会更新,除非在最后,进度从0%增长到100%),尽管一切都进展顺利,例如我的
print
语句正确地呈现在我的服务器日志中)

在上面引用的片段中,有一个注释:

不过请注意,一些WSGI中间件可能会中断流媒体,所以请注意 在调试环境中使用分析器和其他东西时要小心 您可能已经启用了

这可能是问题所在吗?会有解决办法吗

来自我的jinja2模板的JS代码:

    <script type="text/javascript">
    /* progress bar */
    var source = new EventSource("{{ url_for('BP.run', mylongprocess_id=mylongprocess_id) }}");
    source.onmessage = function(event) {
        console.log(event.data);
        var data = event.data.split("!!");
        var nodeid = data[0];
        var process = data[1];
        var process_status = data[2];
        var postpro = data[3];
        var postpro_status = data[4];
        $('.pb1').css('width', process+'%').attr('aria-valuenow', process);   
        $('.pb2').css('width', postpro+'%').attr('aria-valuenow', process);   
        document.getElementById("process_status").innerHTML = process_status;
        document.getElementById("postpro_status").innerHTML = postpro_status;
        document.getElementById("nodeid").innerHTML = nodeid;
        if (postpro >= 100) {
            setTimeout(function() {
                console.log("progress is finished!");
                document.getElementById("status").innerHTML = "redirecting to {{url_for('.view_sonix_result', mylongprocess_id=mylongprocess_id)}}";
                window.location.replace("{{url_for('.terminate_analysis', mylongprocess_id=mylongprocess_id)}}");
                                  }, 2); // / setTimeout function
            } // /if
        else {
            document.getElementById("status").innerHTML = "pending...";
            } // /else
        } // /function
</script>

/*进度条*/
var source=neweventsource(“{url_for('BP.run',mylongprocess_id=mylongprocess_id)}”);
source.onmessage=函数(事件){
console.log(事件数据);
var data=event.data.split(“!!”);
var nodeid=数据[0];
var过程=数据[1];
var过程_状态=数据[2];
var postpro=数据[3];
var postpro_状态=数据[4];
$('.pb1').css('width',process+'%').attr('aria-valuenow',process);
$('.pb2').css('width',postro+'%').attr('aria-valuenow',process);
document.getElementById(“进程状态”).innerHTML=进程状态;
document.getElementById(“postpro_状态”).innerHTML=postpro_状态;
document.getElementById(“nodeid”).innerHTML=nodeid;
如果(postro>=100){
setTimeout(函数(){
log(“进度已完成!”);
document.getElementById(“status”).innerHTML=“重定向到{{url\'u for('.view\'u sonix\'u result',mylongprocess\'u id=mylongprocess\'u id)}}”;
replace({{url_for('.terminate_analysis',mylongprocess_id=mylongprocess_id)}});
},2);///setTimeout函数
}///如果
否则{
document.getElementById(“status”).innerHTML=“待定…”;
}///其他
}///函数
我的(简化)视图:

@BP.route(“/run/”)
@需要登录
def运行(mylongprocess_id):
mylongprocess=mylongprocess.query.get\u或\u 404(mylongprocess\u id)
project=project.query.get\u或\u 404(mylongprocess.project\u id)
检查权限(当前用户、项目“用户”、404)
A、 lcs=_创建_分析(MyLong流程)
@复制\u当前\u请求\u上下文
def gen(MyLong进程、节点ID、存储路径):
打印('正在运行%s“%A”)
对于A.runiterator(lcs)中的(loopnb、total_循环、pct、lclabel):
打印('ran%d/%d(%.1f%%)“%s”'%(loopnb,total_循环,
pct,LCT(标签)
进度=('数据:%s!!%f!!%s!!%f!!%s\n\n'%
(节点ID、pct、lclabel、0,“正在等待…”)
产量进度
打印('正在对%s“%A”进行后处理)
postpro=加载节点(存储路径,节点id=节点id)
对于postpro.\u builditer(target='web'中的步骤、总计、pct、操作,
buildfile=None):
进度=('数据:%s!!%f!!%s!!%f!!%s\n\n'%
(节点ID,100,‘正常’,pct,动作。替换(“”,“”)
产量进度
打印('正在终止%s“%A”)
_终止分析(A,MyLong流程)
返回响应(gen(mylongprocess,mylongprocess.nodeid),mimetype='text/event stream')

当您的流量托管在Pythonywhere上时,它将通过nginx代理,nginx缓冲响应,除非另有规定

为了让一切都顺利进行

  • 给出您的烧瓶响应标题
    response.headers['X-Accel-Buffering']='no'
  • 在您要生成的字符串的末尾有一个
    '\n'
    ,因为python也会缓冲到行尾
  • @BP.route('/run/<int:mylongprocess_id>')
    @login_required
    def run(mylongprocess_id):
        mylongprocess = mylongprocess.query.get_or_404(mylongprocess_id)
        project = Project.query.get_or_404(mylongprocess.project_id)
        check_rights(current_user, project, 'user', 404)
        A, lcs = _create_analysis(mylongprocess)
        @copy_current_request_context
        def gen(mylongprocess, nodeid, store_path):
            print('now runing %s' % A)
            for (loopnb, total_loops, pct, lclabel) in A.runiterator(lcs):
                print('ran %d/%d (%.1f%%) "%s"' % (loopnb, total_loops,
                                                   pct, lclabel))
                progress = ('data: %s!!%f!!%s!!%f!!%s\n\n' %
                            (nodeid, pct, lclabel, 0, 'waiting...'))
                yield progress
            print('now postprocessing %s' % A)
            postpro = load_node(store_path, node_id=nodeid)
            for step, total, pct, action in postpro._builditer(target='web',
                                                           buildfile=None):
                progress = ('data: %s!!%f!!%s!!%f!!%s\n\n' %
                            (nodeid, 100, 'ok',  pct, action.replace('_', ' ')))
                yield progress
            print('now terminating %s' % A)
            _terminate_analysis(A, mylongprocess)
    
        return Response(gen(mylongprocess, mylongprocess.nodeid), mimetype='text/event-stream')