Flask的使用03

flask的cbv

上传文件的cbv

import os
from flask import Flask,views,render_template,request
app = Flask(__name__)
@app.route("/",redirect_to='/index')
def test():
    return ""
class IndexView(views.MethodView):
    def get(self):
        return render_template('index.html')
    def post(self):
        print(request.files)
        my_file=request.files.get("MyFile")
        my_file_path = os.path.join("file", my_file.filename)
        my_file.save(my_file_path)
        return 'ok'
app.add_url_rule('/index','index',view_func=IndexView.as_view(name="index")) #(name='index'反向生成url别名
app.run('0.0.0.0','5000',debug=True)

前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>首页</h1>
<form action="" method="post" enctype="multipart/form-data">
    <input type="file" name="MyFile">
    <input type="submit" value="上传">
</form>
</body>
</html>

限制文件大小,

以及应该循环存文件

上传文件

哦,上传文件可是个经典的好问题了。文件上传的基本概念实际上非常简单, 他基本是这样工作的:

  1. 一个 标签被标记有 enctype=multipart/form-data ,并且在里面包含一个 标签。
  2. 服务端应用通过请求对象上的 files 字典访问文件。
  3. 使用文件的 save() 方法将文件永久地保存在文件系统上的某处。

一点点介绍

让我们建立一个非常基础的小应用,这个小应用可以上传文件到一个指定的文件夹里, 然后将这个文件显示给用户。让我们看看这个应用的基础代码:

import os
from flask import Flask, request, redirect, url_for
from werkzeug import secure_filename

UPLOAD_FOLDER = '/path/to/the/uploads'
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

首先我们导入一些东西,大多数内容都是直接而容易的。werkzeug.secure_filename()将会在稍后进行解释。 UPLOAD_FOLDER 是我们储存上传的文件的地方,而 ALLOWED_EXTENSIONS 则是允许的文件类型的集合。然后我们手动为应用添加一个的 URL 规则。我们通常很少这样做,但是为什么这里要如此呢?原因是我们希望实际部署的服务器 (或者我们的开发服务器)来为我们提供这些文件的访问服务,所以我们只需要一个规则用来生成指向这些文件的 URL 。

为什么我们限制上传文件的后缀呢?您可能不希望您的用户能够上传任何文件到服务器上,如果服务器直接将数据发送给客户端。以这种方式,您可以确保您的用户不能上传可能导致 XSS 问题(参考 跨站脚本攻击(XSS) )的 HTML 文件。也确保会阻止 .php 文件以防其会被运行。当然,谁还会在服务器上安装 PHP 啊,是不是? ????

下一步,就是检查文件类型是否有效、上传通过检查的文件、以及将用户重定向到已经上传好的文件 URL 处的函数了:

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['file']
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return redirect(url_for('uploaded_file',
                                    filename=filename))
    return '''
    <!doctype html>
    <title>Upload new File</title>
    <h1>Upload new File</h1>
    <form action="" method=post enctype=multipart/form-data>
      <p><input type=file name=file>
         <input type=submit value=Upload>
    </form>
    '''

那么 secure_filename() 函数具体做了那些事呢?现在的问题是,有一个信条叫做“永远别相信你用户的输入” ,这句话对于上传文件的文件名也是同样有效的。所有提交的表单数据都可以伪造,而文件名本身也可能是危险的。在摄氏只需记住: 在将文件保存在文件系统之前,要坚持使用这个函数来确保文件名是安全的。

关于文件名安全的更多信息

您对 secure_filename() 的具体工作和您没使用它会造成的后果感兴趣?试想一个人可以发送下列信息作为 filename 给您的应用:

filename = "../../../../home/username/.bashrc"

假定 …/ 的数量是正确的,而您会将这串字符与 UPLOAD_FOLDER 所指定的路径相连接,那么这个用户就可能有能力修改服务器文件系统上的一个文件,而他不应该拥有这种权限。这么做需要一些关于此应用情况的技术知识,但是相信我, 骇客们都有足够的耐心 ????

现在我们来研究一下这个函数的功能:

>>> secure_filename('../../../../home/username/.bashrc')
'home_username_.bashrc'

现在还有最后一件事没有完成: 提供对已上传文件的访问服务。 在 Flask 0.5 以上的版本我们可以使用一个函数来实现此功能:

from flask import send_from_directory

@app.route('/uploads/<filename>')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'],
                               filename)

或者,您也可以选择为 uploaded_file 注册 build_only 规则,然后使用SharedDataMiddleware 类来实现下载服务。这种方法同时支持更老版本的 Flask:

from werkzeug import SharedDataMiddleware
app.add_url_rule('/uploads/<filename>', 'uploaded_file',
                 build_only=True)
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
    '/uploads':  app.config['UPLOAD_FOLDER']
})

运行应用,不出意外的话,一切都应该像预期那样工作了。

改进上传功能
欢迎关注
Flask的使用03--专栏
0.6 新版功能.

Flask 到底是如何处理上传的呢?如果服务器相对较小,那么他会先将文件储存在网页服务器的内存当中。否则就将其写入一个临时未知(如函数 tempfile.gettempdir() 返回的路径)。但是怎么指定一个文件大小的上限,当文件大于此限制,就放弃上传呢? 默认 Flask 会很欢乐地使用无限制的空间,但是您可以通过在配置中设定 MAX_CONTENT_LENGTH键的值来限制它:

from flask import Flask, Request

app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024

上面的代码将会把上传文件限制为最大 16 MB 。 如果请求传输一个更大的文件, Flask 会抛出一个 RequestEntityTooLarge 异常。

这个特性是在 Flask 0.6 中被加入的,但是更老的版本也可以通过构建请求对象的子类来实现。更多信息请查询 Werkzeug 文档中文件处理部分的内容。

上传文件,并限制大小

import os
from flask import Flask,render_template,request
app=Flask(__name__)
UPLOAD_FOLDER='upload'
app.config["UPLOAD_FOLDER"]=UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH']=16 * 1024 * 1024
@app.route("/upload",methods=['GET','POST'])
def upload():
    if request.method=='GET':
        return render_template('upload.html')

    if request.method=='POST':
        print(request.files)
        my_file = request.files.get("MyFile")
        my_file_path = os.path.join(app.config['UPLOAD_FOLDER'], my_file.filename)
        my_file.save(my_file_path)
        return 'ok'

app.run('0.0.0.0','5000',debug=True)

flask的session使用

pip install flask-session

原理:将flask 的session 存储到别的数据库中,但是用的时候还是原框架的session

from flask import Flask,request,render_template,session
from flask_session import Session
from redis import Redis
from wtforms import Form,validators,widgets
from wtforms.fields import simple,core


app = Flask(__name__)
app.config["DEBUG"] = True
app.config["SESSION_TYPE"] = "redis"
app.config["SESSION_REDIS"] = Redis(host="127.0.0.1",port=6379)
app.config["SESSION_COOKIE_NAME"] = "#¥%……&*(#E$RTY$%RTY%^Y&U%^&*"

Session(app)
# app.config["SECRET_KEY"] = "#$%^&*()"

@app.route("/login",methods=["GET","POST"])
def login():
    login_form = LoginForm()
    if request.method == "GET":
        return render_template("login.html", lf=login_form)
    else:
        login_form_data = LoginForm(request.form)
        if login_form_data.validate():
            session["user"] = login_form_data.data.get("username")
            return str(session["user"])
        else:
            return render_template("login.html", lf=login_form_data)

mytry.py

from flask import Flask, render_template,views,session,request,redirect
from flask_session import Session
from redis import Redis
app=Flask(__name__)
# app.default_config
app.config['DEBUG']=True
app.config['SESSION_TYPE']='redis'
app.config['SESSION_REDIS']=Redis(host='127.0.0.1',port=6379)
# 这里是flask_session的Session进行实例化
Session(app)
@app.route("/")
def index():
    return 'this is index '
@app.before_request
def is_login():
    print(1111)
    if request.path=="/login":
        return
    if session.get("user"):
        return None
    return redirect('/login')
class LoginView(views.MethodView):
    def get(self):
        return render_template('login.html')
    def post(self):
        session['user']=request.form.get('username')
        print(request.form.to_dict())
        return 'ok man!'
app.add_url_rule('/login',endpoint='login',view_func=LoginView.as_view(name='login'))
app.run('0.0.0.0','5000')

结果:访问别的视图 检查 没有session就跳转到登录

Flask的使用03--专栏

当然 也可以设置redis密码

app.config['SESSION_TYPE'] = 'redis'  # session类型为redis
app.config['SESSION_PERMANENT'] = False  # 如果设置为True,则关闭浏览器session就失效。
app.config['SESSION_USE_SIGNER'] = False  # 是否对发送到浏览器上session的cookie值进行加密
app.config['SESSION_KEY_PREFIX'] = 'session:'  # 保存到session中的值的前缀
app.config['SESSION_REDIS'] = redis.Redis(host='127.0.0.1', port='6379', password='123123')  # 用于连接redis的配置

redis有隐藏的很深的bug不能暴露到公网的端口 否则 分分钟沦为肉鸡

wtforms

一、简单介绍flask中的wtforms

WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证。

安装:

pip3 install wtforms

生成登录 注册的field

id ==>

StringField 普通的文本输入框

DataRequired ==>是否必填,没填的话抛出错误

Length(min=4,max=8,message=“用户名必须大于等于4,小于等于8”) #长度校验

PasswordField ==>密码输入框

SelectField ==>单选框

SelectMultipleField ==>多选框

SelectField ==>单选框

    gender = core.SelectField(
        label="性别",
        choices=(
            (1, "女"),
            (2, "男")
        ),
        default=1,
        # widget=widgets.RadioInput(),
        render_kw={"class": "my_class"}, #这是到页面中的类
        coerce=int
    )

SelectMultipleField ==>多选框

choices=(
            (1, "python"),
            (2, "java"),
            (3, "c"),
            (4, "sql")
        ),
coerce=int,
default=(1, 4)



from flask import Flask, render_template,views,session,request,redirect
from flask_session import Session
from wtforms import Form,validators,widgets
from wtforms.fields import simple,core
from redis import Redis
app=Flask(__name__)
# app.default_config
app.config['DEBUG']=True
app.config['SESSION_TYPE']='redis'
app.config['SESSION_REDIS']=Redis(host='127.0.0.1',port=6379)
# 这里是flask_session的Session进行实例化
Session(app)

class LoginForm(Form):
    username = simple.StringField(  #==>id="username"页面中
        label="用户名:",
        validators=[
            validators.DataRequired(message="用户名不能为空"),
            validators.Length(min=4,max=8,message="用户名必须大于等于4,小于等于8")
        ]
    )

    password = simple.PasswordField(
        label="密码:",
        validators=[
            validators.DataRequired(message="密码不能为空"),
            validators.Length(min=4, max=8, message="密码必须大于等于4,小于等于8")
        ]
    )


@app.route("/")
def index():
    return 'this is index '
# @app.before_request
# def is_login():
#     print('before_request',"判断是否有session的")
#     if request.path=="/login":
#         return
#     if session.get("user"):
#         return None
#     return redirect('/login')
@app.route('/login',methods=['GET','POST'])
def login():
    login_form = LoginForm()
    if request.method=='GET':
        return render_template('login.html',lf=login_form)
    if request.method=='POST':
        session['user']=request.form.get('username')
        print(request.form.to_dict())
        return 'ok man!'
@app.route('/reg',methods=['GET','POST'])
def reg():
    reg_form = RegForm()
    if request.method=='GET':
        return render_template('reg.html',rf=reg_form)
    if request.method=='POST':
        reg_form_data = RegForm(request.form)
        if reg_form_data.validate():
            session["user"] = reg_form_data.data.get("username")
            return str(session["user"])
        else:
            return render_template("reg.html", rf=reg_form_data)

class RegForm(Form):
    username=simple.StringField(
        label='用户名',
        validators=[
            validators.DataRequired(message='用户名必填!'),
            validators.Length(min=6,max=12,message='用户名长度在6-12之间')
        ]
    )
    password=simple.PasswordField(
        label='密码',
        validators=[
            validators.DataRequired(message='密码必填!'),
            validators.Length(min=6,max=12,message='密码长度在6-12之间')
        ]
    )
    repassword=simple.PasswordField(
        label='确认密码',
        validators=[
            validators.EqualTo("password", message="两次密码不一致")
        ]
    )
    gender = core.SelectField(
        label="性别",
        choices=(
            (1, "女"),
            (2, "男")
        ),
        default=1,
        # widget=widgets.RadioInput(),
        render_kw={"class": "my_class"},
        coerce=int
    )

    email = simple.StringField(
        label="邮箱",
        validators=[
            validators.DataRequired(message="邮箱不能为空"),
            validators.Email(message="不符合规定")
        ]
    )

    hobby = core.SelectMultipleField(
        label="癖好",
        choices=(
            (1, "python"),
            (2, "java"),
            (3, "c"),
            (4, "sql")
        ),
        # widget=widgets.,
        coerce=int,
        default=(1, 4)
    )
app.run('0.0.0.0','5000')

login.html

<body>
<form action="" method="post"  novalidate>
    <p>{{ lf.username.label }}{{ lf.username }}{{ lf.username.errors.0 }}</p>
    <p>{{ lf.password.label }}{{ lf.password }}{{ lf.password.errors[0] }}</p>
    <input type="submit" value="登录">
</form>
</body>

reg.html

<body>
<form action="" method="post" novalidate>
    {% for field in rf %}
    {{ field.label }}, {{ field }}{{ field.errors.0 }}
{% endfor %}
    <input type="submit">
</form>
</body>

相关文章:

  • 2021-09-26
  • 2021-09-26
  • 2021-09-28
  • 2021-07-01
  • 2021-06-24
  • 2021-09-24
  • 2021-04-03
猜你喜欢
  • 2021-06-14
  • 2021-10-27
  • 2021-11-30
  • 2022-03-02
  • 2022-01-04
  • 2021-10-22
  • 2022-12-23
相关资源
相似解决方案