我想用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应用的本质就是:
- 浏览器发送一个HTTP请求;
- 服务器收到请求,生成一个HTML文档;
- 服务器把HTML文档作为HTTP响应的Body发送给浏览器;
- 浏览器收到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'<h1>Hello, web!</h1>']
==application() ==函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:
- environ:一个包含所有HTTP请求信息的
dict 对象; - start_response:一个发送HTTP响应的函数。
在函数中的 start_response() 就表示发送了HTTP响应, 响应状态码为200
- 通常情况下,都应该把
Content-Type 头发送给浏览器。其他很多常用的HTTP Header也应该发送。 - 然后,函数的返回值
b'<h1>Hello, web!</h1>' 将作为HTTP响应的Body发送给浏览器。
application()函数的调用 是由 WSGI服务器来调用的。
运行WSGI服务
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, web!</h1>']
def applicationPath_INFO(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
body = '<h1>Hello, %s!</h1>' % (environ['PATH_INFO'][1:] or 'web')
return [body.encode('utf-8')]
这里的读取URL路径参数 %s 这些,其实类似于 springMVC中获取http请求参数一样,使用 @PathVariable(“id”) 完成。
- 编写的 server.py,负责启动WSGI服务器,它会去加载运行这个Http应用程序,监听 其中的environ 和 start_response
from wsgiref.simple_server import make_server
from hello import application, applicationPath_INFO
httpd = make_server('', 8081, applicationPath_INFO)
print('Serving HTTP on port 8081...')
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 '<h1>Home</h1>'
@app.route('/signin', methods=['GET'])
def signin_form():
return '''<form action="/signin" method="post">
<p><input name="username"></p>
<p><input name="password" type="password"></p>
<p><button type="submit">Sign In</button></p>
</form>'''
@app.route('/signin', methods=['POST'])
def signin():
if request.form['username']=='admin' and request.form['password']=='password':
return '<h3>Hello, admin! x</h3>'
return '<h3>Bad username or password. </h3>'
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框架还有:
小结
有了Web框架,我们在编写Web应用时,注意力就从WSGI处理函数转移到URL+对应的处理函数,这样,编写Web App就更加简单了。
在编写URL处理函数时,除了配置URL外,从HTTP请求拿到用户数据也是非常重要的。Web框架都提供了自己的API来实现这些功能。
Flask通过request.form['name'] 来获取表单的内容。
1.3、使用模板开发Web
使用Web 框架,我们不用自己写 WSGI接口对应的每个 url http请求处理函数,去处理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模式改写一下:
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
用来显示首页的模板:
<html>
<head>
<title>Home</title>
</head>
<body>
<h1 style="font-style:italic">Home</h1>
</body>
</html>
form.html
用来显示登录表单的模板:
<html>
<head>
<title>Please Sign In</title>
</head>
<body>
{% if message %}
<p style="color:red">{{ message }}</p>
{% endif %}
<form action="/signin" method="post">
<legend>Please sign in:</legend>
<p><input name="username" placeholder="Username" value="{{ username }}"></p>
<p><input name="password" placeholder="Password" type="password"></p>
<p><button type="submit">Sign In</button></p>
</form>
</body>
</html>
signin-ok.html
登录成功的模板:
<html>
<head>
<title>Welcome, {{ username }}</title>
</head>
<body>
<p>Welcome, {{ username }}!</p>
</body>
</html>
登录失败的模板呢?我们在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 %}
<a href="/page/{{ i }}">{{ i }}</a>
{% 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.】
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('开始调用了......')
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)
Y = np.array(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)
'''
ValueError: Expected 2D array, got 1D array instead:
array=[10 20 30].
'''
predicted = lrModel.predict(X)
plt.scatter(X, Y, marker='x')
plt.plot(X, predicted, c = 'r')
plt.xlabel("x")
plt.ylabel("y")
plt.show()
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def home():
return '<h1>Home</h1>'
@app.route('/line', methods=['GET'])
def line_form():
return '''<form action="/line" method="post">
<p><input name="username"></p>
<p><input name="password"></p>
<p><button type="submit">Sign In</button></p>
</form>'''
@app.route('/line', methods=['POST'])
def line():
x = request.form['username']
y = request.form['password']
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('调用成功')
if request.form['username']=='admin' and request.form['password']=='password':
return '<h3>Hello, admin! </h3>'
return '<h3>Bad username or password. </h3>'
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 代码的修改:
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('开始调用了......')
print("前端传递给的 x 值为:", x)
print("前端传递给的 y 值为:", y)
X = np.array(x).reshape(-1,1)
Y = np.array(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)
plt.scatter(X, Y, marker='x')
plt.plot(X, predicted, c = 'r')
plt.xlabel("x")
plt.ylabel("y")
plt.savefig('static/LineDemo03.png')
plt.show()
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 '<h1>Home</h1>'
@app.route('/line', methods=['GET'])
def line_form():
return '''<form action="/line" method="post">
<p><input name="xValue"></p>
<p><input name="yValue"></p>
<p><button type="submit">Load numbers</button></p>
</form>'''
@app.route('/line', methods=['POST'])
def line():
x = request.form['xValue']
y = request.form['yValue']
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、使用模板开发、视图跳转
修改之后的 WSGI 函数:
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('开始调用了......')
print("前端传递给的 x 值为:", x)
print("前端传递给的 y 值为:", y)
X = np.array(x).reshape(-1,1)
Y = np.array(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)
plt.scatter(X, Y, marker='x')
plt.plot(X, predicted, c = 'r')
plt.xlabel("x")
plt.ylabel("y")
plt.savefig('static/LineDemo03.png')
plt.show()
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('/lineFigure', methods=['POST'])
def line():
x = request.form['xValue']
y = request.form['yValue']
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:
{#首页表单#}
<html>
<head>
<meta charset="UTF-8">
<title>Home</title>
<body>
<h1 style="font-style:italic">Home</h1>
</body>
</html>
lineIndex.html模板:
{#线性回归上传数据表单#}
<html>
<head>
<meta charset="UTF-8">
<title>Please Load Numbers</title>
</head>
<body>
<form action="/lineFigure" method="post">
<legend>Please load numbers:</legend>
<p><input name="xValue" placeholder="XValue" value="{{ xValue }}"></p>
<p><input name="yValue" placeholder="YValue" value="{{ yValue }}"></p>
<p><button type="submit">Load numbers</button></p>
</form>
</body>
</html>
lineFigure.html模板:
{#展示线性回归的图片结果界面#}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flask Show Image</title>
</head>
<body>
{# <img style="width:180px" src="data:;base64,{{ img_stream }}">#}
<img src="data:;base64,{{ img_stream }}">
</body>
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() 函数
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/<filename>')
def files(filename):
return send_from_directory('./upload', filename, as_attachment=True)
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:
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<h2>Upload file</h2>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" class="input">
<input type="submit" value="upload" class="input button">
</form>
<h2>Download file</h2>
<ol>
{% for entry in entries %}
<li><a href='/files/{{entry}}'>{{entry}}</a>
{% endfor %}
</ol>
</body>
</html>
界面效果如下:
-
我们在 web app 程序中有 return render_template(‘uploadIndex.html’, entries = entries) 。后端通过 entries 传递给了前端 ,且利用 for entry in entries 循环语句,显示 {{entry}} 即upload文件夹下的文件和文件夹。 -
点击 upload 进行提交, submit 让表单去访问 “/upload” ,完成文件上传的功能。 即访问 @app.route(’/upload’, methods=[‘POST’]) -
从 flask 模块中引入 request 对象,request 对象中的属性 files 记录了上传文件的相关信息。 f = request.files[‘file’]
uploadSucess.html 文件上传界面:
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<h1>上传成功</h1>
<a href='/'>返回主页</a>
</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}} 这个是 后端传递给前端的数据。
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<h1>上传成功</h1>
<a href='/'>返回主页</a>
<h1>线性回归模型预测</h1>
<body>
<form action="/analysis/linearRegression" method="post">
<div>
<p style="color:red"><input name="filename" value={{fileName}}></p>
<input type="submit" value="Linear Regression Analysis" class="input button">
</div>
</form>
</body>
</html>
页面结果如下所示:
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']
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))
return render_template('uploadSucess.html',fileName = new_filename)
else:
return jsonify({"errno": 1001, "errmsg": "上传失败"})
@app.route('/files/<filename>')
def files(filename):
return send_from_directory('./upload', filename, as_attachment=True)
@app.route("/example/linear")
def example():
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)
@app.route("/download_example")
def downloadExample():
print("下载案例数据集")
dirpath = os.path.join(app.root_path, 'static')
filename = 'LinearRegression_data.csv'
return send_from_directory(dirpath, filename, as_attachment=True)
def LineProcesser(filePre, fileName):
print('开始调用了......')
dirpath = os.path.join(app.root_path, filePre, fileName)
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 = x.reshape(-1,1)
y = y[0]
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)
plt.scatter(x, y, marker='x')
plt.plot(x, predicted, c = 'r')
plt.xlabel("x")
plt.ylabel("y")
filenamePre = os.path.splitext(fileName)[0]
savepath = os.path.join(app.root_path, 'results', filenamePre)
img_path = savepath + '.png'
plt.savefig(img_path)
plt.show()
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']
LineProcesser(filePre = 'upload', fileName = fileName)
print('用户分析成功')
filenamePre = os.path.splitext(fileName)[0]
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:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>file upload</title>
</head>
<h1>Upload file</h1>
<body>
<form id="form1" method="post" action="/upload" enctype="multipart/form-data">
<div>
<input id="File1" type="file" name="myfile"/>
<input type="submit" value="upload" class="input button">
</div>
</form>
<h2>Download file</h2>
<ol>
{% for entry in entries %}
<li><a href='/files/{{entry}}'>{{entry}}</a>
{% endfor %}
</ol>
<h2>Example</h2>
<form action="/example/linear">
<div>
<input type="submit" value="linear regression example" class="input button"/>
</div>
</form>
<h2>Download example data</h2>
<form action="/download_example">
<div>
<input type="submit" value="Download data" class="input button"/>
</div>
</form>
</body>
</html>
uploadSucess.html:
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<h1>上传成功</h1>
<a href='/'>返回主页</a>
<h1>线性回归模型预测</h1>
<body>
<form action="/analysis/linearRegression" method="post">
<div>
<p style="color:red"><input name="filename" value={{fileName}}></p>
<input type="submit" value="Linear Regression Analysis" class="input button">
</div>
</form>
</body>
</html>
LineFigure.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flask Show Image</title>
</head>
<body>
<img src="data:;base64,{{ img_stream }}">
</body>
运行结果 如下所示:
仍然还要补足的地方:
- 多个图片的保存和展现
- 上传成功后进行分析的时候,界面逻辑展示 是否需要更改下。【如何简短地 前后端数据传递】
|