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

How to stream (yield) text data to jinja2 template for a flask app on pythonanywhere

我尝试在我的 Pythonanywhere 帐户上实施 "streaming contents"。

它看起来或多或少与此处显示的内容相同: 比照。 http://flask.pocoo.org/docs/0.10/patterns/streaming/

除了我的视图正在计算一个可能持续一分钟的复杂过程并将其数据生成到我的模板,其中脚本应该更新一些进度条(''source.onmessage'')。

这在我的开发机器上运行良好,但在我的 pythonanywhere 帐户上运行不正常。在这台服务器上,进程看起来卡住了(进度条永远不会更新,除了在最后从 0% 突然增长到 100%),尽管一切都在引擎盖下进行,例如 我的 print 语句正确呈现到我的服务器日志中)。

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

Note though that some WSGI middlewares might break streaming, so be careful there in debug environments with profilers and other things you might have enabled.

会不会是这里的问题?会有解决方法吗?

来自我的 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>

我的(简化)观点:

@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')

当您的流量托管在 PythonAnywhere 上时,您的流量会通过 nginx 代理,除非另有说明,否则 nginx 会缓冲响应。

为了让所有东西都冲洗干净,

  1. 给出你的烧瓶回复headersresponse.headers['X-Accel-Buffering'] = 'no'
  2. 在您生成的字符串末尾有一个 '\n',因为 python 也会缓冲到行尾。