栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Python+Flask框架:一个简单的web-app

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Python+Flask框架:一个简单的web-app

我想用python、Flask框架、html去开发一个线性回归预测模型的 web service。
version1.0:简单思路如下,

  • 前端传递后台数据
  • 后端读取数据,调用线性回归函数,完成该数据模型的预测
  • 将模型预测的结果-图片返回给前端【图片保存,转化为字符串形式 返回给指定页面,页面跳转】

关于flask框架的学习,可以看这个链接:https://www.cnblogs.com/zhaopanpan/p/9033100.html

1、创建一个简单的Flask 框架

参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017805733037760

1.1、WSGI接口

一个Web应用的本质就是:

  1. 浏览器发送一个HTTP请求;
  2. 服务器收到请求,生成一个HTML文档;
  3. 服务器把HTML文档作为HTTP响应的Body发送给浏览器;
  4. 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。

使用Python来进行web开发,关于 HTTP请求、发送响应这些,由专门的服务器软件去实现。 我们需要使用python专注于去生成一个 HTML文档。在这里使用 WSGI接口去提供统一的这个http请求、解析服务。

  • WSGI:Web Server Gateway Interfac

代码:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'Hello, web!']

==application()==函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

  • environ:一个包含所有HTTP请求信息的dict对象;
  • start_response:一个发送HTTP响应的函数。

在函数中的 start_response() 就表示发送了HTTP响应, 响应状态码为200

  • 通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。
  • 然后,函数的返回值b'Hello, web!'将作为HTTP响应的Body发送给浏览器。

application()函数的调用 是由 WSGI服务器来调用的。

运行WSGI服务
  • 我们自己编写的WSGI处理函数:
# hello.py
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'Hello, web!']

# 读取URL路径参数
def applicationPath_INFO(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    body = 'Hello, %s!' % (environ['PATH_INFO'][1:] or 'web')
    return [body.encode('utf-8')]

这里的读取URL路径参数 %s 这些,其实类似于 springMVC中获取http请求参数一样,使用 @PathVariable(“id”) 完成。

  • 编写的 server.py,负责启动WSGI服务器,它会去加载运行这个Http应用程序,监听 其中的environ 和 start_response
# server.py
# 从wsgiref模块导入:
from wsgiref.simple_server import make_server
# 导入我们自己编写的application函数:
from hello import application, applicationPath_INFO

# 创建一个服务器,IP地址为空,端口是5000,处理函数是application:
# httpd = make_server('', 5000, application)
httpd = make_server('', 8081, applicationPath_INFO)
print('Serving HTTP on port 8081...')
# 开始监听HTTP请求:
httpd.serve_forever()

运行程序,打开浏览器测试: http://localhost:8081/

小结

对于一个web应用程序,入口其实就是一个 WSGI处理函数。

  • HTTP请求的所有输入信息都可以通过environ获得
  • HTTP响应的输出都可以通过start_response()加上函数返回值作为Body。
1.2、使用Web框架

一个Web App,其实就是一个 WSGI的处理函数。它去针对每一个HTTP请求 作处理 并响应。在这里,需要解决的是 如何处理100个不同的URL。

  • 每一个URL 会有不同的 http请求方法,请求路径 url path_Info等等。【GET/POST/DELETe/PUT】

  • 解决方法可以是 进行适配分析, switch 类似的,一一对应

    • def application(environ, start_response):
          method = environ['REQUEST_METHOD']
          path = environ['PATH_INFO']
          if method=='GET' and path=='/':
              return handle_home(environ, start_response)
          if method=='POST' and path='/signin':
              return handle_signin(environ, start_response)
          ...
      
    • 上面这样,不利于维护,会在原有代码基础上进行增加修改。所以,再次抽象,类似于 工厂模式,适配器模式 创建接口函数

  • 解决方案是:我们专注于用一个函数处理一个URL,至于URL到函数的映射,就交给Web框架来做

在这里,采用 flask 框架:

$ pip install flask

写一个app.py,处理3个URL,分别是:

  • GET /:首页,返回Home;
  • GET /signin:登录页,显示登录表单;
  • POST /signin:处理登录表单,显示登录结果。

注意噢,同一个URL/signin分别有GET和POST两种请求,映射到两个处理函数中。

创建框架
from flask import Flask
from flask import request

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def home():
    return 'Home'

@app.route('/signin', methods=['GET'])
def signin_form():
    return '''

''' @app.route('/signin', methods=['POST']) def signin(): # 需要从request对象读取表单内容: #x = request.form['username'] #print(x) if request.form['username']=='admin' and request.form['password']=='password': return 'Hello, admin! x' return 'Bad username or password. ' if __name__ == '__main__': app.run()

运行python app.py,Flask自带的Server在端口5000上监听:

打开浏览器,输入首页地址http://localhost:5000/:

首页显示正确!

再在浏览器地址栏输入http://localhost:5000/signin,会显示登录表单:

输入预设的用户名admin和口令password,登录成功:

输入其他错误的用户名和口令,登录失败:

实际的Web App应该拿到用户名和口令后,去数据库查询再比对,来判断用户是否能登录成功。

除了Flask,常见的Python Web框架还有:

  • Django:全能型Web框架;
  • web.py:一个小巧的Web框架;
  • Bottle:和Flask类似的Web框架;
  • Tornado:Facebook的开源异步Web框架
小结

有了Web框架,我们在编写Web应用时,注意力就从WSGI处理函数转移到URL+对应的处理函数,这样,编写Web App就更加简单了。

在编写URL处理函数时,除了配置URL外,从HTTP请求拿到用户数据也是非常重要的。Web框架都提供了自己的API来实现这些功能。

Flask通过request.form['name']来获取表单的内容

1.3、使用模板开发Web

使用Web 框架,我们不用自己写 WSGI接口对应的每个 url http请求处理函数,去处理URL

  • 只要专心写 URL处理函数就行。

Web Service 展现的是 获取 URL 信息和数据后,进行逻辑处理,再展示给用户。函数返回时一般是一个包含 HTML 的字符串。 对于复杂的页面,HTML 不仅要正确还要 进行CSS美化,再加上Javascript脚本去实现各种交互和动画效果。在python中直接生成HTML页面难度比较大。

使用模板, 我们就是要预先准备一个HTML文档。 在这个文档中,会嵌入一些变量和指令,然后,根据我们传入的数据进行解析,得到最终的HTML页面,发送给用户:

上面的这个就是MVC:Model-View-Controller。

  • C:Controller,控制器。Python中处理URL的函数就是控制器,负责业务逻辑。
  • V:View,View负责显示逻辑。包含变量{{ name }}的模板就是V。View的输出结果就是用户看到的HTML。
  • Model:模型。这里的Model就是用来传给View的。这样View在替换变量的时候,就可以直接从Model中取出相应的数据。【业务层、数据层,即对应着java开发中的 service层、dao层】

上面的例子中,Model就是一个dict:

{ 'name': 'Michael' }

只是因为Python支持关键字参数,很多Web框架允许传入关键字参数,然后,在框架内部组装出一个dict作为Model。

现在,我们把上次直接输出字符串作为HTML的例子用高端大气上档次的MVC模式改写一下:

# Web MVC 框架
# Flask通过render_template()函数来实现模板的渲染。和Web框架类似,Python的模板也有很多种。Flask默认支持的模板是jinja2,
# 所以我们先直接安装jinja2: $ pip install jinja2
# 然后,开始编写jinja2模板: home.html

from flask import Flask, request, render_template

app = Flask(__name__)

# 首页
@app.route('/', methods=['GET', 'POST'])
def home():
    return render_template('home.html')

# 登录表单
@app.route('/signin', methods=['GET'])
def signin_form():
    return render_template('form.html')

# 登录成功表单
@app.route('/signin', methods=['POST'])
def signin():
    username = request.form['username']
    password = request.form['password']
    if username=='admin' and password=='password':
        return render_template('signin-ok.html', username=username)
    return render_template('form.html', message='Bad username or password', username=username)

if __name__ == '__main__':
    app.run()

此时就已经使用 **render_template() 函数**来实现模板的渲染。

Flask通过render_template()函数来实现模板的渲染。和Web框架类似,Python的模板也有很多种。Flask默认支持的模板是jinja2,所以我们先直接安装jinja2:

$ pip install jinja2

然后,开始编写jinja2模板:

home.html

用来显示首页的模板:



  Home


  

Home

form.html

用来显示登录表单的模板:



  Please Sign In


  {% if message %}
  

{{ message }}

{% endif %}
Please sign in:

signin-ok.html

登录成功的模板:



  Welcome, {{ username }}


  

Welcome, {{ username }}!

登录失败的模板呢?我们在form.html中加了一点条件判断,把form.html重用为登录失败的模板。

最后,一定要把模板放到正确的templates目录下,templates和app.py在同级目录下:

运行 程序,http://127.0.0.1:5000/signin

通过MVC,我们在Python代码中处理M:Model和C:Controller,而V:View是通过模板处理的,这样,我们就成功地把Python代码和HTML代码最大限度地分离了。

使用模板的另一大好处是,模板改起来很方便,而且,改完保存后,刷新浏览器就能看到最新的效果,这对于调试HTML、CSS和Javascript的前端工程师来说实在是太重要了。

在Jinja2模板中,我们用=={{ name }}==表示一个需要替换的变量。很多时候,还需要循环、条件判断等指令语句,在Jinja2中,用{% ... %}表示指令。

比如循环输出页码:

{% for i in page_list %}
    {{ i }}
{% endfor %}

如果page_list是一个list:[1, 2, 3, 4, 5],上面的模板将输出5个超链接。

除了Jinja2,常见的模板还有:

  • Mako:用<% ... %>和${xxx}的一个模板;
  • Cheetah:也是用<% ... %>和${xxx}的一个模板;
  • Django:Django是一站式框架,内置一个用{% ... %}和{{ xxx }}的模板。
小结

有了MVC,我们就分离了Python代码和HTML代码。HTML代码全部放到模板里,写起来更有效率。

2、线性回归 Web app demo

线性回归 web-app Demo的功能:

  • 前端传递 x值,y值
  • 后台读取前台数据,然后调用 线性回归函数 LineProcesser()。
  • 返回给前端显示页面,展现给用户
2.1、使用Web框架开发

在这里,我首先模仿前面的 Web 框架开发进行代码编写。类似于首页登录,那么我在这里去获取前端传递的参数:

  • 获取 x 和 y 的参数值
  • x 和y 前端过来的,应该是字符串,我将其转化为 整数列表
  • 获得了这两个 x ,y 后,我去调用 线性回归函数 LineProcesser(),进行绘图查看结果
  • 先查看能不能获取数据,正确地绘图。
  • 绘图展示。 利用了matplotlib 库中的函数

编写 LineDemo03.py 代码,使用 print()输出,是为了测试 看代码可以运行到哪里。

  • 线性回归函数 LinearRegression()的例子链接:
    • https://blog.csdn.net/qq_38328378/article/details/80775351
    • https://www.cnblogs.com/learnbydoing/p/12190168.html 【查看这一部分去解决的 error:Reshape your data either using array.reshape(-1, 1) if your data has a single feature or array.reshape(1, -1) if it contains a single sample.】
# 线性回归 web

from flask import Flask
from flask import request
import string
# 导入线性回归
import LinearRegression

# 线性回归运算
import numpy as np
from pandas import read_csv;
from matplotlib import pyplot as plt;
from sklearn.linear_model import LinearRegression

def LineProcesser(x, y):
    print('开始调用了......')
    # 从前台获取数据 x 和 y
    print("前端传递给的 x 值为:", x)
    print("前端传递给的 y 值为:", y)
    
    '''
    当 x 具有单个特征的时候,error:Reshape your data either using array.reshape(-1, 1) 
    if your data has a single feature or array.reshape(1, -1) if it contains a single sample.
    '''
    X = np.array(x).reshape(-1,1)
    #X.reshape(-1, 1)
    Y = np.array(y)

    # 第二步,画出散点图,求x和y的相关系数
    #plt.plot(x, y)
    plt.scatter(x, y)
    plt.show()
    # 第三步,估计模型参数,建立回归模型
    #lrModel = LinearRegression()
    lrModel = LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
    # 训练模型
    lrModel.fit(X, Y)
    # 第四步、对回归模型进行检验
    lrModel.score(X, Y)

    # 第五步、利用回归模型进行预测
    #lrModel.predict([[50], [40], [30]]) # 这里的是线性回归模型进行预测,预测的几个点的值。 但这个是二维数组,所以会报错
    '''
    ValueError: Expected 2D array, got 1D array instead:
    array=[10 20 30].
    '''
    predicted = lrModel.predict(X) #使用模型预测

    # 绘制散点图 参数:x横轴 y纵轴
    plt.scatter(X, Y, marker='x')
    plt.plot(X, predicted, c = 'r')
    # 绘制x轴和y轴坐标
    plt.xlabel("x")
    plt.ylabel("y")
    # 显示图形
    plt.show()

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def home():
    return 'Home'

#  读取前端的一串数字
@app.route('/line', methods=['GET'])
def line_form():
    return '''

''' @app.route('/line', methods=['POST']) def line(): # 获取前台提供的数据点 x = request.form['username'] y = request.form['password'] #print(x) # 分割字符串 xStr = x.split(',') yStr = y.split(',') # 前台进入的数据全部是字符型,需要转换. 将整数添加到列表中,然后用列表中的数据去进行计算。 将得到的结果返回。 可以将图片转化成流的形式,传递给前端 strX = []; strY = []; for i in range(len(xStr)): strX.append( int(xStr[i]) ) strY.append(int(yStr[i])) print(strX) # print(type(strX)) #print(type(strX[0])) print(strY) # 调用线性回归 LineProcesser(x = strX, y = strY); print('调用成功') # 需要从request对象读取表单内容: if request.form['username']=='admin' and request.form['password']=='password': return 'Hello, admin! ' return 'Bad username or password. ' if __name__ == '__main__': app.run()

运行,测试:http://127.0.0.1:5000/

测试,后台能否获取数据,并调用 Line函数进行线性回归拟合 绘图展示 :http://127.0.0.1:5000/line

点击 sigin后,会去运行 @app.route(’/line’, methods=[‘POST’]) 函数,可以看到在后台的数据结果:

[20, 30, 40]
[50, 60, 80]
开始调用了......
前端传递给的 x 值为: [20, 30, 40]
前端传递给的 y 值为: [50, 60, 80]
127.0.0.1 - - [14/Oct/2021 17:00:04] "POST /line HTTP/1.1" 200 -
调用成功

后台输出 传递数据, 调用成功的标记。输出了 调用成功,且图片展示了。

  • 此时我想把图片结果展现给前台。 第一个想法就是 把图片保存在 static 静态资源中,然后直接去获取这个 static下的图片文件即可
  • 保存图片,并显示给前端。 参考链接:https://www.jianshu.com/p/9aa1b5180c23
  • 保存图片到本地。 参考链接:https://blog.csdn.net/qq_42845522/article/details/118604527
    • https://www.jianshu.com/p/ddc7c43253b2
python flask将读取的图片返回给web前端

参考链接:https://www.jianshu.com/p/9aa1b5180c23

重点需要注意的地方:

1、open(img_local_path, ‘r’) ,这样不会显示图片,正确的为:open(img_local_path, ‘rb’)
2、然后最关键的是:将这句base64.b64encode(img_stream)后加上.decode(),作用是把格式转为字符串。【应该类似于字节流吧,然后便于传输】

LineDemo03.py 代码的修改:

# 线性回归 web

from flask import Flask, app
from flask import request
from flask import render_template #

import numpy as np
from matplotlib import pyplot as plt;
# 线性回归运算
from sklearn.linear_model import LinearRegression

def LineProcesser(x, y):
    print('开始调用了......')
    # 从前台获取数据 x 和 y, 并对数据作数组或矩阵处理
    print("前端传递给的 x 值为:", x)
    print("前端传递给的 y 值为:", y)
    X = np.array(x).reshape(-1,1)
    Y = np.array(y)
    # 第二步,画出散点图,求x和y的相关系数
    #plt.plot(x, y)
    plt.scatter(x, y)
    #plt.savefig('E:\test01.png')
    plt.show()
    # 第三步,估计模型参数,建立回归模型
    #lrModel = LinearRegression()
    lrModel = LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
    # 训练模型
    lrModel.fit(X, Y)
    # 第四步、对回归模型进行检验
    lrModel.score(X, Y)

    # 第五步、利用回归模型进行预测
    predicted = lrModel.predict(X) #使用模型预测

    # 绘制散点图 参数:x横轴 y纵轴
    # plt.figure(figsize=(8, 6), dpi=600) 3这个dpi=600, 影响用户体验
    plt.scatter(X, Y, marker='x')
    plt.plot(X, predicted, c = 'r')
    # 绘制x轴和y轴坐标
    plt.xlabel("x")
    plt.ylabel("y")
    # 保存图片,显示图形
    plt.savefig('static/LineDemo03.png')
    #plt.savefig('E:\test02.png')
    plt.show()


# Flask读取服务器本地图片,并返回图片流给前端显示
def return_img_stream(img_local_path):
    """
    工具函数:
    获取本地图片流
    :param img_local_path:文件单张图片的本地绝对路径
    :return: 图片流
    """
    import base64
    img_stream = ''
    with open(img_local_path, 'rb') as img_f: #
        img_stream = img_f.read()
        img_stream = base64.b64encode(img_stream).decode()
    return img_stream

#
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def home():
    return 'Home'

#  读取前端的一串数字
@app.route('/line', methods=['GET'])
def line_form():
    return '''

''' @app.route('/line', methods=['POST']) def line(): # 获取前台提供的数据点 x = request.form['xValue'] y = request.form['yValue'] #print(x) # 分割字符串 xStr = x.split(',') yStr = y.split(',') # 前台进入的数据全部是字符型,需要转换. 将整数添加到列表中,然后用列表中的数据去进行计算。 将得到的结果返回。 # 可以将图片转化成流的形式,传递给前端 strX = []; strY = []; for i in range(len(xStr)): strX.append( int(xStr[i]) ) strY.append(int(yStr[i])) print(strX) print(strY) # 调用线性回归 LineProcesser(x = strX, y = strY) print('调用成功') img_path = 'static/LineDemo03.png' img_stream = return_img_stream(img_path) return render_template('LineFigure.html', img_stream=img_stream) if __name__ == '__main__': app.run()

运行,测试:http://127.0.0.1:5000/line

结果:

改进点:

  • 初次登录页面的显示
  • http://127.0.0.1:5000/line 后调用完后去进行视图跳转,跳转到运行界面
  • 其它几个WSGI 的URL处理函数的 模板改进
2.2、使用模板开发、视图跳转
  • 使用模板开发: HTML模板,将html显示和 python 控制程序分开。

  • 视图跳转,其实就是在这个页面完成某个动作后,去跳转到其它页面,或者进行更新

    • Please load numbers:

    • 这里的就是 Load numbers 组件按钮,动作为 submit时,会去触发 跳转到 /lineFigure 这个URL处理函数中。

修改之后的 WSGI 函数:

# 线性回归 web
from flask import Flask, app
from flask import request
from flask import render_template 

import numpy as np
from matplotlib import pyplot as plt;
from sklearn.linear_model import LinearRegression # 线性回归运算

def LineProcesser(x, y):
    print('开始调用了......')
    # 从前台获取数据 x 和 y, 并对数据作数组或矩阵处理
    print("前端传递给的 x 值为:", x)
    print("前端传递给的 y 值为:", y)
    X = np.array(x).reshape(-1,1)
    Y = np.array(y)
    
    # 第二步,画出散点图,求x和y的相关系数
    plt.scatter(x, y)
    plt.show()
    # 第三步,估计模型参数,建立回归模型
    lrModel = LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
    # 训练模型
    lrModel.fit(X, Y)
    # 第四步、对回归模型进行检验
    lrModel.score(X, Y)

    # 第五步、利用回归模型进行预测
    predicted = lrModel.predict(X) #使用模型预测

    # 绘制散点图 参数:x横轴 y纵轴
    plt.scatter(X, Y, marker='x')
    plt.plot(X, predicted, c = 'r')
    # 绘制x轴和y轴坐标
    plt.xlabel("x")
    plt.ylabel("y")
    # 保存图片,显示图形
    plt.savefig('static/LineDemo03.png')
    #plt.savefig('E:\test02.png')
    plt.show()

# Flask读取服务器本地图片,并返回图片流给前端显示
def return_img_stream(img_local_path):
    """
    工具函数:
    获取本地图片流
    :param img_local_path:文件单张图片的本地绝对路径
    :return: 图片流
    """
    import base64
    img_stream = ''
    with open(img_local_path, 'rb') as img_f:
        img_stream = img_f.read()
        img_stream = base64.b64encode(img_stream).decode()
    return img_stream


app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def home():
    return render_template('lineHome.html')

#  读取前端的一串数字
@app.route('/lineIndex', methods=['GET'])
def line_form():
    return render_template('lineIndex.html')

#@app.route('/lineIndex', methods=['POST'])
@app.route('/lineFigure', methods=['POST'])
def line():
    # 获取前台提供的数据点
    x = request.form['xValue']
    y = request.form['yValue']
    #print(x)
    # 分割字符串
    xStr = x.split(',')
    yStr = y.split(',')
    # 前台进入的数据全部是字符型,需要转换.  将整数添加到列表中,然后用列表中的数据去进行计算。 将得到的结果返回。
    strX = [];
    strY = [];
    for i in range(len(xStr)):
        strX.append( int(xStr[i]) )
        strY.append(int(yStr[i]))
    
    # 调用线性回归
    LineProcesser(x = strX, y = strY)
    print('调用成功')
    img_path = 'static/LineDemo03.png'
    img_stream = return_img_stream(img_path)
    return render_template('LineFigure.html', img_stream=img_stream)

if __name__ == '__main__':
    app.run()

模板:

lineHome.html:

{#首页表单#}


    
  Home

  

Home

lineIndex.html模板:

{#线性回归上传数据表单#}


    
  Please Load Numbers



    Please load numbers:
    

lineFigure.html模板:

{#展示线性回归的图片结果界面#}




    
    Flask Show Image


{#    #}
    

2.3、上传文件测试

关于上传文件 参考的博客 链接:

https://www.cnblogs.com/nulige/p/13497254.html

https://www.imooc.com/wiki/flasklesson/flaskupload.html

https://www.cnblogs.com/wongbingming/p/6802660.html

自己的大致整体思路:

  • 上传文件(规定文件的格式,先假设只能由 dsc文件的 *.csv 类型, 然后再处理 txt类型的数据)
  • 文件上传成功之后, 获取指定路径下的信息, 读取数据
  • 转换数据形式,调用 后台的 MCBL 代码
  • 还应该解决的地方就是: 关于页面跳转的 url 路径设置 名称【这个后续再定义 参考Calfitter他们做的】
  • 页面渲染的工作
  • 应该有一个例子【例子中的文件,我就直接固定好了指定路径给他, Example】

上传文件测试:

  • 上传文件 包括的几个部分有:
    • upload.py 一个web app
    • 初始化界面模板,进行上传和下载文件
    • 上传成功界面
    • 保存上传文件的 文件夹

upload.py 代码:

  • url 路径 ‘/’ 初始化界面, 视图渲染返回 uploadIndex.html 模板,且把 ./upload 路径 当前路径下的文件和文件夹内容传递给 entries 信息
  • url路径 ‘/upload’ 进行文件上传, 获取 request 请求中的 file 文件信息,进行文件保存,且保存的路径是 /upload 下面,且显示文件上传成功信息
  • /files/ 完成文件下载功能。 使用 flask框架中的 send_from_directory() 函数
#!/usr/bin/python3
from flask import Flask, render_template, request, send_from_directory
import os

app = Flask(__name__)

@app.route('/')
def index():
    entries = os.listdir('./upload')
    return render_template('uploadIndex.html', entries = entries)

@app.route('/upload', methods=['POST'])
def upload():
    f = request.files['file']
    path = os.path.join('./upload', f.filename)
    f.save(path)
    return render_template('uploadSucess.html')

@app.route('/files/')
def files(filename):
    return send_from_directory('./upload', filename, as_attachment=True) 
# 这种的类似于 请求转发。 应该说这就是返回结果,不过是直接调用了 send_from_directory()方法

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

关于 send_from_directory() 函数:flask框架中的

  • flask.send_from_directory(directory,filename,** options )
    • filePath:文件的绝对路径,不包含文件名
    • filename:文件名称
    • as_attachment:是否显示文件名称as_attachment –设置为True是否要发送带有标题的文件。Content-Disposition: attachment 如果设置为False则浏览器返回文件预览 如果该文件可以被浏览器渲染,例如 pdf 图片 等【return send_from_directory(dirpath, filename, as_attachment=False) # as_attachment=True 一定要写,不然会变成打开,而不是下载】

‘/’ 访问时的初始化界面, 前端渲染模板是:uploadIndex.html:




文件上传



Upload file

Download file

    {% for entry in entries %}
  1. {{entry}} {% endfor %}

界面效果如下:

  1. 我们在 web app 程序中有 return render_template(‘uploadIndex.html’, entries = entries) 。后端通过 entries 传递给了前端 ,且利用 for entry in entries 循环语句,显示 {{entry}} 即upload文件夹下的文件和文件夹。

  2. 点击 upload 进行提交, submit 让表单去访问 “/upload” ,完成文件上传的功能。 即访问 @app.route(’/upload’, methods=[‘POST’])

  3. 从 flask 模块中引入 request 对象,request 对象中的属性 files 记录了上传文件的相关信息。 f = request.files[‘file’]

uploadSucess.html 文件上传界面:




文件上传



上传成功

返回主页

返回主页,则直接调用 ‘/’ 会回到初始界面。

下载文件:

  • 访问路径 / 时,处理函数 index 列出目录 upload 下所有的文件名,作为参数传给首页的模板 index.html。
  • 用户点击文件名链接后,即可下载相应的文件。这是调用了 @app.route(’/files/’) ,调用这个wed app函数,实行send_from_directory() 去下载文件

文件路径信息有:

2.4、 文件上传后,获取文件数据进行调用

文件上传成功后,跳转到了成功页面,此时便可以进行 读取文件中的数据,进行分析。

我仍然是拿一个 线性回归拟合 去进行测试。

思路:

  • 我在成功界面进行分析。 那么我要后端调用这个页面的时候 就应该把文件的名称 路径信息 也传递过来
  • 前端读取了这个 文件信息后,去调用一个 web app 完成线性回归,保存图片到指定文件夹,并显示结果(读取文件夹下的图片)。

参考了前后端数据传递的链接:https://blog.csdn.net/weixin_38168694/article/details/88769729

因为有两点:

  • upload 上传文件成功后,我想要在 成功界面 去进行 用户的数据集的分析, 【后端传递给前端,在 @app.route(’/’)
    def index() 这里面】
  • 在成功界面 我要对数据集 进行分析,那么我需要把这个文件的路径 名称信息传递给后端 【前端传递给后端, 在

    中】

html模板:

  • {{fileName}} 这个是 后端传递给前端的数据。



文件上传



上传成功

返回主页

线性回归模型预测

页面结果如下所示:

web app代码:upload.py:这里新加了几个功能

  • allowed_file() 函数 去判断 文件的后缀是否合法的, 即 *.csv 、 *.txt等
  • index() 函数在进行文件上传时, 后端传递给前端 文件的名字信息。 我暂时只是传递一个文件的 【entries, 这里不严谨。多个的话 使用for循环】
  • example() 和 downloadExample() 这两个函数 是我想要展示的一个固定例子。 【给定了指定文件名称。 不用进行上传】
  • analysis_LinearRegression() 这个 app 完成 从前端得到 用户上传的文件名称信息,然后在后端去完成 线性回归拟合模型。
import os
from flask import Flask, render_template, send_from_directory, request, jsonify
import time

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.linear_model import LinearRegression

app = Flask(__name__)

UPLOAD_FOLDER = 'upload'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER  # 设置文件上传的目标文件夹
basedir = os.path.abspath(os.path.dirname(__file__))  # 获取当前项目的绝对路径
ALLOWED_EXTENSIONS = set(['txt', 'csv', 'xls', 'xlsx'])  # 允许上传的文件后缀

# 判断文件是否合法
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

# 具有上传功能的页面
@app.route('/')
def index():
    entries = os.listdir('./upload')
    return render_template('upload.html', entries = entries)

@app.route('/upload', methods=['POST'], strict_slashes=False)
def api_upload():
    file_dir = os.path.join(basedir, app.config['UPLOAD_FOLDER'])  # 拼接成合法文件夹地址
    if not os.path.exists(file_dir):
        os.makedirs(file_dir)  # 文件夹不存在就创建
    f = request.files['myfile']  # 从表单的file字段获取文件,myfile为该表单的name值
    if f and allowed_file(f.filename):  # 判断是否是允许上传的文件类型
        fname = f.filename
        ext = fname.rsplit('.', 1)[1]  # 获取文件后缀
        unix_time = int(time.time())
        new_filename = str(unix_time) + '.' + ext  # 修改文件名   #??? 为什么要改文件名呢
        f.save(os.path.join(file_dir, new_filename))  # 保存文件到upload目录
        return render_template('uploadSucess.html',fileName = new_filename)
    else:
        return jsonify({"errno": 1001, "errmsg": "上传失败"})

# 这个展示所有的文件的信息,是不是可以 省略掉
@app.route('/files/')
def files(filename):
    return send_from_directory('./upload', filename, as_attachment=True) # 这种的类似于 请求转发


##################  线性回归案例  ########################
@app.route("/example/linear")
def example():
    #dirpath = os.path.join(app.root_path, 'staticLinearRegression_data.csv') # 默认给的线性回归数据 文件地址
    filename = 'LinearRegression_data.csv' # 默认给的线性回归数据 文件地址
    LineProcesser(filePre= 'static', fileName= filename)

    img_path = 'results/LinearRegression_data.png'
    img_stream = return_img_stream(img_path)

    return render_template('LineFigure.html', img_stream=img_stream)

# file download
@app.route("/download_example")
def downloadExample():
    print("下载案例数据集")
    dirpath = os.path.join(app.root_path, 'static')  # 这里是下载目录,从工程的根目录写起,比如你要下载static/js里面的js文件,这里就要写“static/js”
    # return send_from_directory(dirpath, filename, as_attachment=False)  # as_attachment=True 一定要写,不然会变成打开,而不是下载
    filename = 'LinearRegression_data.csv'
    return send_from_directory(dirpath, filename, as_attachment=True)  # as_attachment=True  下载

# 线性回归模型
def LineProcesser(filePre, fileName):
    print('开始调用了......')
    dirpath = os.path.join(app.root_path, filePre, fileName) # 线性回归数据 文件地址
    #print("dirpath", dirpath)
    # 从数据集中读取数据
    df = pd.read_csv(dirpath, header=None)
    df = df.T
    x = df.iloc[0, 1:]
    x = x.astype(float)
    x = np.array(x, dtype=np.float32)

    y = df.iloc[1:, 1:]
    y = y.astype(float)
    y = np.array(y, dtype=np.float32)

    # 从前台获取数据 x 和 y, 并对数据作数组或矩阵处理
    #print("数据集中的 x 值为:", x)
    #print("数据集中的 y 值为:", y)
    x = x.reshape(-1,1)
    y = y[0]
    #print(y)
    # 第二步,画出散点图,求x和y的相关系数
    plt.scatter(x, y)
    plt.show()
    # 第三步,估计模型参数,建立回归模型
    lrModel = LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
    # 训练模型
    lrModel.fit(x, y)
    # 第四步、对回归模型进行检验
    lrModel.score(x, y)

    # 第五步、利用回归模型进行预测
    predicted = lrModel.predict(x) #使用模型预测

    # 绘制散点图 参数:x横轴 y纵轴
    plt.scatter(x, y, marker='x')
    plt.plot(x, predicted, c = 'r')
    # 绘制x轴和y轴坐标
    plt.xlabel("x")
    plt.ylabel("y")
    # 保存图片,显示图形
    #path = 'static/' + fileName + '.png'
    filenamePre = os.path.splitext(fileName)[0]
    savepath = os.path.join(app.root_path, 'results', filenamePre) # 线性回归数据 文件地址
    img_path = savepath + '.png'
    #print(img_path)
    #plt.savefig('static/exampleLinearRegression.png')
    plt.savefig(img_path)
    plt.show()

# Flask读取服务器本地图片,并返回图片流给前端显示
def return_img_stream(img_local_path):
    """
    工具函数:
    获取本地图片流
    :param img_local_path:文件单张图片的本地绝对路径
    :return: 图片流
    """
    import base64
    img_stream = ''
    with open(img_local_path, 'rb') as img_f:
        img_stream = img_f.read()
        img_stream = base64.b64encode(img_stream).decode()
    return img_stream


################  文件上传后,进行数据分析   ###################
@app.route('/analysis/linearRegression', methods=['POST'])
def analysis_LinearRegression():
    print("分析用户上传的数据集:start")
    fileName =  request.form['filename']
    #print(fileName)
    LineProcesser(filePre = 'upload', fileName =  fileName) # 提供的线性回归数据集 文件地址
    print('用户分析成功')

    filenamePre = os.path.splitext(fileName)[0] # 只获取名称,去掉后缀 .csv .txt等
    img_path =  os.path.join('results', filenamePre)
    img_path_all = img_path + '.png'
    img_stream = return_img_stream(img_path_all)
    return render_template('LineFigure.html', img_stream=img_stream)


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

关于前端模板:

  • 一个初始化界面, upload.html。包括文件上传、文件下载列表、线性回归例子、线性回归例子数据集下载。
  • 一个上传成功界面。并在这个界面上去完成 线性回归预测分析。
  • 图形展示界面。展示分析的结果

upload.html:






    
    file upload

Upload file

Download file

    {% for entry in entries %}
  1. {{entry}} {% endfor %}

Example

Download example data

uploadSucess.html:




文件上传



上传成功

返回主页

线性回归模型预测

LineFigure.html:





    
    Flask Show Image



    

运行结果 如下所示:

仍然还要补足的地方:

  • 多个图片的保存和展现
  • 上传成功后进行分析的时候,界面逻辑展示 是否需要更改下。【如何简短地 前后端数据传递】
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/530015.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号