Flask - 文件上传

flash 消息

  • 说明:

    • 当用户的状态发生改变时,需要给与提示,警告等信息;通常都是以弹窗的形式出现,指引用户操作。
  • 函数:

    • flash:将指定的内容保存起来,用在视图函数中

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      from flask import Flask, render_template, redirect, url_for, session, flash, get_flashed_messages

      @app.route('/', methods=['GET', 'POST'])
      def index():
      form = NameForm()
      if form.validate_on_submit():
      last_name = session.get('username', None)
      if last_name and last_name != form.name.data:
      flash('大哥,又换签名了?')
      session['username'] = form.name.data
      return redirect(url_for('index'))

      name = session.get('username', None)
      return render_template('form.html', form=form, name=name)
    • get_flashed_messages:获取保存的消息,用在模板文件中

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      {% extends 'bootstrap/base.html' %}

      {% import 'bootstrap/wtf.html' as wtf %}

      {% block content %}
      <div class="container">
      {% for message in get_flashed_messages() %}
      <div class="alert alert-warning alert-dismissible" role="alert">
      <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span>
      </button>
      {{ message }}
      </div>
      {% endfor %}
      </div>
      {% endblock %}
    • 提示:建议粘贴一个可消失的警告框

flask-moment

  • 说明:是一个将时间本地化显示的扩展库,使用非常简单
  • 安装:pip install flask-moment
  • 使用:
    • 视图函数:导入类库,创建对象
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      from flask_moment import Moment


      moment = Moment(app)

      @app.route('/mom/')
      def mom():
      from datetime import datetime, timedelta
      t = datetime.utcnow() + timedelta(seconds=-60)
      return render_template('mom.html', t=t)
    • 模板文件:加载 moment.js 文件,依赖 jQuery
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      {# 加载jQuery #}
      {{ moment.include_jquery() }}
      {# 加载moment.js文件,依赖jQuery #}
      {{ moment.include_moment() }}

      {# 设置语言为中文 #}
      {{ moment.locale('zh-CN') }}

      {# 简化版本的格式化显示 #}
      <div>时间:{{ moment(t).format('LLLL') }}</div>
      <div>时间:{{ moment(t).format('LLL') }}</div>
      <div>时间:{{ moment(t).format('LL') }}</div>
      <div>时间:{{ moment(t).format('L') }}</div>

      {# 自定义显示 #}
      <div>时间:{{ moment(t).format('YYYY-MM-DD') }}</div>

      {# 时间差 #}
      <div>发表于:{{ moment(t).fromNow() }}</div>
  • 时间本地化操作:
    • 显示时间:moment(t).format('字符串')
    • 时间差:moment(t).fromNow()
  • moment 官网

原生的文件上传

  • 添加模板文件,书写一个表单,表单中添加 file 字段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>文件上传</title>
    </head>
    <body>
    <form method="post" action="" enctype="multipart/form-data">
    <input type="file" name="photo">
    <input type="submit" value="上传">
    </form>
    </body>
    </html>
  • 视图函数中根据请求类型判断是否成功提交,然后提取(request.files)上传信息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    from flask import Flask, request, render_template
    from flask_script import Manager
    import os

    # 允许上传的文件后缀
    ALLOWED_EXTENSION = set(['png', 'jpg', 'jpeg', 'gif'])

    app = Flask(__name__)
    # 配置上传文件保存位置
    app.config['UPLOAD_FOLDER'] = os.getcwd()
    # 允许上传的文件大小
    app.config['MAX_CONTENT_LENGTH'] = 1024 * 2
    manager = Manager(app)


    # 检测上传的文件是否是允许的后缀
    def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSION


    @app.route('/')
    def index():
    return '文件上传'


    @app.route('/upload/', methods=['GET', 'POST'])
    def upload():
    if request.method == 'POST':
    # 获取上传文件对象
    file = request.files.get('photo')
    # 类型判断
    if file and allowed_file(file.filename):
    # 拼接文件路径名
    pathname = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
    # 保存文件
    file.save(pathname)
    return '上传成功!'
    else:
    return '上传失败'
    return render_template('upload.html')


    if __name__ == '__main__':
    manager.run()
  • 安全检查:文件大小,文件后缀
  • 练习:保存上传文件时使用随机的文件名
  • 总结:文件上传注意事项
    1. 表单提交方法必须是 POST
    2. 表单必须设置 enctype="multipart/form-data"
    3. 上传文件的字段必须为 file,必须有 name 属性
    4. 是否超过了允许的最大尺寸
    5. 上传文件的临时保存目录是否有权限,是否有空间

flask-uploads

  • 说明:文件上传的扩展的库,提供了类型的限定,校验等功能

  • 安装:pip install flask-uploads

  • 使用:

    1. 创建上传集合对象 (UploadSet)

      1
      2
      3
      4
      from flask_uploads import UploadSet, IMAGES, configure_uploads, patch_request_class

      # 创建上传对象,指定名称及过滤的文件类型
      photos = UploadSet('photos', IMAGES)
    2. 配置上传对象

      1
      2
      # 配置上传对象
      configure_uploads(app, photos)
    3. 设置上传文件尺寸

      1
      2
      3
      4
      5
      # 允许的HTTP请求的大小
      app.config['MAX_CONTENT_LENGTH'] = 1024*1024*128
      # 设置文上传大小,默认为64M,要比HTTP最大请求允许的尺寸小
      # 设置为None时,MAX_CONTENT_LENGTH配置生效
      patch_request_class(app, size=1024 * 1024 * 2)
    4. 配置上传文件保存目录

      1
      2
      # 配置文件保存位置
      app.config['UPLOADED_PHOTOS_DEST'] = os.getcwd()
    5. 保存上传文件 photo.save()

      1
      2
      # 保存上传文件并获取上传名
      filename = photos.save(request.files['photo'])
    6. 获取 URL:photos.url(filename)

      1
      2
      # 获取上传文件的URL
      img_url = photos.url(filename)
  • 生成随机文件名:random_string

    1
    2
    3
    4
    5
    # 生成随机字符创
    def random_string(length=32):
    import random
    base_str = 'abcdefghijklmnopqrstuvwsyz1234567890'
    return ''.join(random.choice(base_str) for i in range(length))

综合上传

  1. flask-uploads
  2. flask-wtf
  3. flask-bootstrap
  4. 生成随机的文件名
  5. 生成缩略图 pip install pillow
  6. flash
  • 示例:

    • 视图函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      from flask import Flask, render_template, flash, get_flashed_messages
      from flask_script import Manager
      from flask_bootstrap import Bootstrap
      from flask_uploads import UploadSet, IMAGES, configure_uploads
      from flask_wtf import FlaskForm
      from wtforms import SubmitField
      from flask_wtf.file import FileField, FileRequired, FileAllowed
      import os
      # PIL只支持Python2.x,开源社区维护了pillow使之支持Python3.x
      from PIL import Image

      app = Flask(__name__)

      app.config['SECRET_KEY'] = '123456'
      app.config['UPLOADED_PHOTOS_DEST'] = os.getcwd()

      manager = Manager(app)
      bootstrap = Bootstrap(app)
      photos = UploadSet('photos', IMAGES)
      configure_uploads(app, photos)


      # 文件上传表单类
      class UploadForm(FlaskForm):
      photo = FileField('上传头像', validators=[FileRequired('文件未选择'), FileAllowed(photos, message='只能上传图片文件')])
      submit = SubmitField('上传')


      def random_string(length=32):
      import random
      base_str = 'abcdefghijklmnopqrstuvwxyz1234567890'
      return ''.join(random.choice(base_str) for i in range(length))


      @app.route('/')
      def index():
      return '完整的文件上传'


      @app.route('/upload/', methods=['GET', 'POST'])
      def upload():
      img_url = None
      form = UploadForm()
      if form.validate_on_submit():
      # 提取后缀
      suffix = os.path.splitext(form.photo.data.filename)[1]
      # 生成随机文件名
      filename = random_string() + suffix
      # 保存上传文件
      photos.save(form.photo.data, name=filename)
      # 拼接完整的文件路径
      pathname = os.path.join(app.config['UPLOADED_PHOTOS_DEST'], filename)
      # 打开上传文件
      img = Image.open(pathname)
      # 重新设置尺寸
      img.thumbnail((128, 128))
      # 保存修改
      img.save(pathname)
      # 获取上传文件的URL
      img_url = photos.url(filename)
      # 保存flush消息
      flash('头像已上传成功')

      return render_template('upload.html', form=form, img_url=img_url)


      if __name__ == "__main__":
      manager.run()
    • 模板文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      {% extends 'bootstrap/base.html' %}

      {% import 'bootstrap/wtf.html' as wtf %}

      {% block title %}完整的文件上传{% endblock %}

      {% block content %}
      {% for message in get_flashed_messages() %}
      <div class="alert alert-success alert-dismissible" role="alert">
      <button type="button" class="close" data-dismiss="alert" aria-label="Close">
      <span aria-hidden="true">&times;</span>
      </button>
      {{ message }}
      </div>
      {% endfor %}

      {% if img_url %}
      <img src="{{ img_url }}" alt="头像">
      {% endif %}

      <div class="container">
      {{ wtf.quick_form(form) }}
      </div>
      {% endblock %}