Flask上传文件

Posted Flask学习笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask上传文件相关的知识,希望对你有一定的参考价值。

Flask上传文件

上传文件包括很多,比如用户头像,文章图片,文件分享等等,它也涉及到很多内容:上传文件,过滤文件类型,限制大小,文件名的编辑,拖拽上传,进度条,文件命名,文件目录的管理等等.

文件上传的基本概念非常简单:

  1. 一个在 html 文档中定义的好的 form标签( <from enctype=multipart/form-data>),并且 form中必须包含一个 <input type=file>标签
  2. 服务端应用通过请求对象上的 request.files字典访问文件.
  3. 使用文件对象的 save()方法将文件永久的保存在文件系统中.

示例代码

https://github.com/ningwenyan/demo_code/tree/master/flask_demo_code/T14

1.flask 原生文件上传

目录结构

file_upload_1 
├── app.py              #    主程序
├── config.py           # 配置文件
├── static                  # css,js文件
├── templates         # 渲染模板文件
│   └── uploads.html               # 上传html文件
└── uploads               # 上传文件的文件夹

uploads.html实现一个简单的上传功能,需要注意的是,必须写enctype="multipart/form-data

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传</title>
</head>
<body>
    <form action="" method="post" enctype="multipart/form-data">
        <table>
            <tr>
                <td>上传文件</td>
                <td><input type="file" name="file"></td>
            </tr>
            <tr>
                <td><label for="#"><input type="submit"></label></td>
            </tr>
        </table>

    </form>
</body>
</html>

config.py

import os

DEBUG=True
TEMPLATES_AUTO_RELOAD=True
par_dir = os.path.dirname(__file__)       
UPLOAD_FOLDER=par_dir+'/uploads/'                   # 指定要上传的文件夹
MAX_CONTENT_LENGTH=16*1024*1024                 # 指定上传的文件的最大大小

app.py

from flask import Flask
import config
from flask import request,render_template,send_from_directory
from werkzeug.utils import secure_filename
import os

app = Flask(__name__)
app.config.from_object(config)

@app.route('/')
def hello_world():
     return 'Hello World!'

ALLOWED_EXTENSIONS = set(['txt','pdf','png','jpg','jpeg','gif'])

# 判断文件后缀
def allowed_file(filename):
     return '.' in filename and filename.rsplit('.',1)[1in ALLOWED_EXTENSIONS

# 定义上传文件路由
@app.route('/uploads/',methods=['GET','POST'])
def upload_file():
       if request.method == 'GET':
           return  render_template('uploads.html')
     else:
        # 文件类型不能用 request.form.get() 获取
        # 要使用 request.files.get() 方法
        file = request.files.get('file')
        # 可以通过 文件对象的 filename 属性拿到文件名
        if file and allowed_file(file.filename):
            # 出于安全考虑,使用 security filename
            filename = secure_filename(file.filename)
            print(app.config['UPLOAD_FOLDER'])
            file.save(os.path.join(app.config['UPLOAD_FOLDER'],filename))
          return render_template('uploads.html')

# 定义访问上传文件路径
@app.route('/uploads/<filename>')
def uploads_file(filename):
     # 调用 send_from_directory 方法提供对已上传文件的访问服务
     return send_from_directory(app.config['UPLOAD_FOLDER'],filename)

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

需要注意的是:

  • 必须手动验证上传的文件后缀,不符合的不上传
  • 使用 request.file.get() 方法获取文件,而不是 request.form.get(),虽然文件处于表单中,但是文件定义在另外的方法中
  • file.save() 方法来指定保存文件的最终路径
  • 使用 security_filename 来处理文件名,不能相信用户上传的任何东西
  • 可以定义回显来上传的文件,这个需要 send_from_directory 方法的支持.

2.Flask-Uploads

Flask-Uploads 简化加强了flask 的原生上传功能.

Flask-Uploads 中有一个UploadSet 的概念,用他来统一验证要上传的文件类型,以及文件名称的安全问题.

1.安装

$ pip install flask-upload

2.基本概念

  1. 文件类型过滤

设置文件上传的路径和文件类型:(在config.py文件中指定)

  • UPLOADED_FILES_DEST: 指定上传文件保存路径
  • UPLOADED_FILES_URL:指定上传文件的URL
  • UPLOADED_FILES_ALLOW:指定允许上传的文件
  • UPLOAD_FILES_DENY:指定不允许上传的文件

注意:Flask-Uploads 可以设置不同的set ,所以以上FILES 的名称不是绝对的,而是指定UploadSet(name='FILES')  的name字段.这个名称可以根据自己上传文件的不同,而指定不同的名称,比如:如果是图片可以随意命名.

# config.py
UPLOADED_IMG_DEST=xxx
UPLOADED_IMG_ALLOW=tuple('jpg jpe jpeg png gif svg bmp'.split())

# app.py
UploadSet(name='IMG')

UPLOADED_IMG_ALLOW=tuple('jpg jpe jpeg png gif svg bmp'.split()) 是自定义的文件类型的集合,也就是说,自定义文件类型必须是tuple类型,而且必须是以上格式.

Flask-Uploads中也有一些内置的类型:

TEXT = ('txt',)
DOCUMENTS = tuple('rtf odf ods gnumeric abw doc docx xls xlsx'.split())
IMAGES = tuple('jpg jpe jpeg png gif svg bmp'.split())
AUDIO = tuple('wav mp3 aac ogg oga flac'.split())
DATA = tuple('csv ini json plist xml yaml yml'.split())
SCRIPTS = tuple('js php pl py rb sh'.split())
ARCHIVES = tuple('gz bz2 zip tar tgz txz 7z'.split())
EXECUTABLES = tuple('so exe dll'.split())
DEFAULTS = TEXT + DOCUMENTS + IMAGES + DATA

通过导入即可使用,已经可以满足基本的使用.

from flask_uploads import TEXT,DOCUTMENTS,...
  • 如果对内置的类型中有些文件不允许上传,可以使用 UPLOADED_X_DENY 来拒绝使用.
  • 如果要指定多种类型,可以使用 UPLOADED_IMG_ALLOW=(IMGAGE+DOCUMENTS) 来扩展
  1. 文件名隐患

不用使用secure_filename() 来处理文件名,直接使用UploadSet().save(request.files.get('photo')) 方法来安全保存文件.photoinput 标签的name 元素.

  1. 限制文件大小
from flask_uploads import UploadSet,configure_uploads,patch_request_class
patch_request_class(app,32*1024*1024)

使用patch_request_class()  函数绑定app 来限制大小.

  1. 获取文件

UploadSet().url(filename) 函数可以获取上传文件的URL 路径,

3.UploadSet()

Flask-Uploads 的核心类

UploadSet(name='files', extensions=('txt''rtf''odf''ods''gnumeric''abw''doc''docx''xls''xlsx''jpg''jpe''jpeg''png''gif''svg''bmp''csv''ini''json''plist''xml''yaml''yml'), default_dest=None)

photos = UploadSet('PHOTO')

它包含3个参数

  • name :指定文件上传 Set 的集合,需要配合 config.py 文件中的 UPLOADED_X_DESTX字段
  • extensions:指定上传类型,可以被 config.py 中的 UPLOADED_X_ALLOWUPLOADED_X_DENY覆盖.
  • default_dest:指定上传的路径,可以被 config.py 中的 UPLOADED_X_DEST覆盖

注册配置好的UploadSet() 要使用方法config_uploads(app, photos)

4.Demo

$ tree
file_upload_2
├── app.py
├── config.py
├── static
├── templates
│   ├── show.html
│   └── upload.html
└── uploads
    └── test.doc

upload.html

    <form action="" method="post" enctype="multipart/form-data">
        <table>
            <tr>
                <td>上传文件</td>
                <td><input type="file" name="photo"></td>
            </tr>
            <tr>
                <td><label for="#"><input type="submit"></label></td>
            </tr>
        </table>
    </form>

show.html

<img src="{{ url }}" alt="{{ name }}">

config.py

import os
from flask_uploads import IMAGES,DOCUMENTS

DEBUG=True
TEMPLATES_AUTO_RELOAD=True
Dir = os.path.dirname(os.path.abspath(__file__))
# 指定上传目录
UPLOADED_PHOTO_DEST=Dir+'/uploads/'
# 指定运行文件类型
# IMAGES = tuple('jpg jpe jpeg png gif svg bmp'.split())
UPLOADED_PHOTO_ALLOW=(IMAGES+DOCUMENTS)

app.py

from flask import Flask,request,redirect,url_for,render_template,abort
from flask_uploads import UploadSet,configure_uploads,patch_request_class
import config

app = Flask(__name__)
app.config.from_object(config)

@app.route('/')
def hello_world():
    return 'Hello World!'

# 限定上传文件大小
patch_request_class(app,32*1024*1024)

# 约束上传文件类型和位置
photos = UploadSet('PHOTO')
# 注册到app中
configure_uploads(app, photos)

@app.route('/uploads/',methods=['GET','POST'])
def upload():
    if request.method == 'POST' and 'photo' in request.files:
        filename = photos.save(request.files.get('photo'))
        return redirect(url_for('show',name=filename))
    return render_template('upload.html')

@app.route('/uploads/<name>',methods=['GET','POST'])
def show(name):
    if name is None:
        return abort(404)
    # 通过url 方法获取到文件的 URL路径
    url = photos.url(name)
    return render_template('show.html',url=url,name=name)

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


以上是关于Flask上传文件的主要内容,如果未能解决你的问题,请参考以下文章

java Ftp上传创建多层文件的代码片段

用flask做了个文件管理,上传速度太慢了

flask jQuery ajax 上传文件

如何在Python框架Flask中将图像文件从表单上传到数据库

使用Python和Flask上传到Google云端硬盘时,文件大小为零

16Flask实战第16天:Flask文件上传