Flask做一个网页shell界面

最近在做PyOne的后台管理程序,在做上传文件功能的时候,在想怎么展示上传进度比较合适,想来想去,还是觉得后台展示什么数据,前端就怎么展示好了,其实就是偷懒!但是这个懒也不是那么好偷的,因为要把后台运行的结果直接展示在前台的话,就需要一个__伪shell__环境展示,搜索了几天看到两种方法:

  1. 定时ajax获取结果。这种方法弊端很多,一是数据获取不及时,展示上效果也很不好;二是ajax获取数据就需要在后台生成文件,将结果保存下来。各种弊端下,这种方法就pass了
  2. 通过websocket,由服务端推送数据。这种方法其实是最适合的,服务端只要把后台的数据推送至前端即可。但是由于之前在另外一个项目中尝试使用websocket,但是对websocket掌握的不够,效果并不好,于是也放弃了websocket。

在不断的google中,最终让我看到一篇文章,并使用该方法完美的解决了我的诉求。

EventSource介绍

由于我不是专业的程序员,而且我也是今天才知道EventSource这种东东,就直接复制其他博客的内容上来,大家大致了解一下就好

服务端推 ,指的是由服务器主动的向客户端发送消息(响应)。在应用层的HTTP协议实现中,“请求-响应”是一个round trip,它的起点来自客户端,因此在应用层之上无法实现简易的服务端推功能。当前解决服务端推送的方案有这几个:

  1. 客户端长轮询
  2. websocket双向连接
  3. iframe永久帧

长轮训虽然可以避免短轮训造成的服务端过载,但在服务端返回数据后仍需要客户端主动发起下一个长轮训请求,等待服务端响应,这样仍需要底层的连接建立而且服务端处理逻辑需要相应处理,不符合逻辑上的流程简单的服务端推送;

websocket连接相对而言功能最强大,但是它对服务器的版本有要求,在可以使用websocket协议的服务器上尽量采用此种方式;

iframe永久帧则是在在页面嵌入一个专用来接受数据的iframe页面,该页面由服务器输出相关信息,如,服务器不停的向iframe中写入类似的script标签和数据,实现另一种形式的服务端推送。不过永久帧的技术会导致主页面的加载条始终处于“loading”状态,体验很差。

HTML5规范中提供了服务端事件 EventSource ,浏览器在实现了该规范的前提下创建一个EventSource连接后,便可收到服务端的发送的消息,这些消息需要遵循一定的格式,对于前端开发人员而言,只需在浏览器中侦听对应的事件皆可。

相比较上文中提到的3中实现方式, EventSource流的实现方式对客户端开发人员而言非常简单,兼容性上出了IE系的浏览器(IE、Edge)外其他都良好;对于服务端,它可以兼容老的浏览器,无需upgrade为其他协议,在简单的服务端推送的场景下可以满足需求。 在浏览器与服务端需要强交互的场景下,websocket仍是不二的选择。

利用EventSource做一个伪shell

由于这里使用Python flask作为服务端,因此以下代码均为flask。

安装依赖包

依赖包主要是4个:

  • flask:web框架
  • shelljob:subprocess的封装包,提供更简便的操作
  • eventlet:异步框架
  • gunicorn:运行框架
pip install flask shelljob eventlet gunicorn

代码

服务端:server.py

# -*- coding=utf-8 -*-
from flask import Flask,Response,send_file
from shelljob import proc

import eventlet
eventlet.monkey_patch()

app = Flask(__name__)

@app.route( '/stream' )
def stream():
    g = proc.Group()
    p = g.run( [ "bash", "-c", "for ((i=0;i<10;i=i+1)); do echo $i; sleep 1; done" ] )

    def read_process():
        while g.is_pending():
            lines = g.readlines()
            for proc, line in lines:
                yield "data:" + line + "\n\n"
        yield "data:end\n\n"

    return Response( read_process(), mimetype= 'text/event-stream' )

@app.route('/page')
def get_page():
    return send_file('page.html')

if __name__ == "__main__":
    app.run(debug=True)

前端页面:page.html

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <h1>Output</h1>
    <div id="output"></div>
    <script>
    var source = new EventSource("/stream");
    source.onmessage = function(event) {
        if(event.data=='end'){
            source.close();
        }
        else{
        document.getElementById("output").innerHTML += event.data + "<br/>";
        }
    }
    </script>
</body>

</html>

运行命令

server.pypage.html放在同目录,运行:

gunicorn -k eventlet -b 0:8000 server:app

运行效果:

20180912_184005.gif

效果预览

上面的代码只是一个简单demo,下面的是真正运用到PyOne的效果VYJDMU[I5O]GM$@LL3HN8{9.png

本文作者:Abbey

本文链接:https://www.abbeyok.com/archives/60

版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0许可协议。转载请注明出处!

【伪正版】Sublime Text 3截... <<
0 条评论

请先登陆注册

已登录,注销 取消