Flask实现上传文件

概述

本文通过 注册用户 功能讲解上传文件功能

官方上传例子:
https://dormousehole.readthedocs.io/en/latest/patterns/fileuploads.html

html代码

<form action="/reg" method="post" enctype="multipart/form-data">
    用户名: <input type="text" name="username"><br/>
    密码: <input type="password" name="password"><br/>
    头像:<input type="file" name="img">
    <input type="submit" value="登录">
</form>

注意:

  1. 表单必须使用post方法

  2. 表单必须添加 enctype="multipart/form-data"属性

  3. 上传文件的代码是:<input type="file" name="img">,注意name是img

python代码

# coding=utf-8

from flask import Flask,request

app = Flask(__name__)

'''
指定要上传的文件的最大大小是1M, 超出大小会抛出RequestEntityTooLarge 异常
浏览器显示413错误
'''
app.config['MAX_CONTENT_PATH']=1024*1024

@app.route('/reg',methods=['POST'])
def reg():
    username=request.form.get('username')
    password=request.form.get('password')
    print('username=',username)
    print('password=',password)
    f=request.files.get('img')
    print(app.root_path)

    if f.filename!='':
        path=os.path.join(app.static_folder,f.filename)
        f.save(path)
    return '注册成功!'


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

注意:
代码 f=request.files.get('img') 获取html控件名是img的文件,返回的是 FileStorage 类对象

配置上传文件的最大大小

app.config['MAX_CONTENT_PATH']=1024*1024

指定要上传的文件的最大大小是1M, 超出大小会抛出RequestEntityTooLarge 异常
浏览器显示413错误

配置参见:
http://www.pythondoc.com/flask/config.html
http://docs.jinkan.org/docs/flask/patterns/fileuploads.html

FileStorage类

属性

属性名 说明
filename 上传文件的名字
name html中的控件名,本例中是img
headers 请求头信息
content_length 文件长度
content_type 文件类型

save()方法

将上传文件保存到指定位置

语法:
save(dst)
参数说明:
  • dst:将上传文件保存到指定的位置,路径+文件名

FileStorage 更多信息参见官方网址:https://werkzeug.palletsprojects.com/en/0.15.x/datastructures/#werkzeug.datastructures.FileStorage

比较完美的上传

上例的缺陷:

  1. 在上例中,上传文件都保存在工程根目录下,当上传文件较多时,工程根目录下就会特别杂乱

  2. 在上例中,什么文件都能上传,如果恶意用户上传 html文件、 .exe病毒,会对服务器造成很严重的损害

  3. 假设李雷注册时上传头像文件名是:1.jpg,之后韩梅梅注册时上传头像文件名也是:1.jpg,按照上例的写法,就会寒梅梅的1.jpg覆盖李雷的1.jpg

  4. 如果恶意用户上传几个G的文件,会严重影响服务器

解决方法:

  1. 一般上传文件会单独放在一个文件夹中(如:upload文件夹),这样让工程目录和文件更加清晰

  2. 上传时做校验,因为注册时只能上传图片,所以只允许上传 jpg、png 等文件,不允许上传 其他格式文件,尤其是 .exe文件,防止用户上传 病毒

  3. 保存是将 文件改名,用uuid做文件名,注意:文件后缀不改变

  4. 限制上传大小

python代码如下:

# coding=utf-8

from flask import Flask,request,flash
import os,uuid

# 上传文件夹
UPLOAD_FOLDER='upload'

# 允许上传的文件格式
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg'])

app = Flask(__name__)

'''
上传文件限制为 5 M 。如果上传了大于这个尺寸的文件, Flask 会抛 出一个 RequestEntityTooLarge 异常。
但在本地开发服务器中,可能连接重置,在生产服务器中不会发生这种情况
'''
app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024

@app.route('/reg',methods=['POST'])
def reg():
    username=request.form.get('username')
    password=request.form.get('password')
    print('username=',username)
    print('password=',password)
    f=request.files.get('img')
    print(f == None)
    print(app.root_path)

    if f.filename=='' :
        return '请上传头像'
    if allowed_file(f.filename):
        path=os.path.join(app.config['UPLOAD_FOLDER'],get_new_filename(f.filename))
        f.save(path)
        return '注册成功!'
    else:
        return '只允许上传 %s 格式的文件'%str(ALLOWED_EXTENSIONS)[1:-1]


def get_upload_folder():
    '''
    在static目录下,如果upload文件夹不存在就创建
    返回上传文件夹路径
    '''
    path=os.path.join(app.static_folder,UPLOAD_FOLDER)
    if not os.path.exists(path):
        os.makedirs(path)
    return path

def allowed_file(filename):
    '''
    根据文件名后缀判断,该文件是否运行上传
    '''
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def get_new_filename(filename):
    '''
    生成新文件名,将uuid作为文件名,后缀不变
    '''
    id=str(uuid.uuid1()).replace('-','')
    newfilename=id+filename[filename.rindex('.'):]
    return newfilename

if __name__ == "__main__":
    # 将上传文件夹添加到app的config中
    app.config['UPLOAD_FOLDER'] = get_upload_folder()
    app.run(debug=True)

原文出处:/show_1EF3UUlzllEU.html