14

Flask-Dropzone:为你的Flask程序添加文件上传功能

 3 years ago
source link: https://greyli.com/flask-dropzone/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Flask-Dropzone:为你的Flask程序添加文件上传功能

这篇文章属于“Flask常用扩展介绍系列”,这个系列的文章目录索引可以在《Flask常用扩展介绍系列文章索引》看到。

某天在Stack Overflow上看到一个关于Dropzone.js的问题,就去研究了一下,写了一个回答,顺便写了一个小demo。我发现,如果有一个集成Dropzone.js到Flask,并且简化设置步骤的扩展,肯定要比其他上传方式简单的多——于是就有了Flask-Dropzone

Flask-Dropzone在模板中提供了一些方法来帮助你创建上传区域,引入相关资源。你只需要添加一些配置就可以实现上传类型的过滤,文件大小限制,上传后跳转等功能。当然,你还要自己编写视图函数来处理和保存文件,并进行服务器端的二次验证。

《Flask Web开发实战》中的第3个示例程序(图片社交程序Albumy)使用了这个扩展。

$ pip install flask-dropzone

和其他扩展类似,你可以通过实例化Dropzone类,并传入程序实例app进行初始化:

from flask_dropzone import Dropzone
app = Flask(__name__)
dropzone = Dropzone(app)

在使用工厂函数创建程序实例时,你也可以使用init_app()方法:

from flask_dropzone import Dropzone
dropzone = Dropzone()
def create_app():
    app = Flask(__name__)
    dropzone.init_app(app)
    return app

引入Dropzone.js资源

你需要自己手动编写引入Dropzone.js的CSS和JavaScript资源的语句。在开发时,或对于玩具项目,你可以可以使用Flask-Dropzone提供的两个快捷方法:

<head>
    {{ dropzone.load_css() }}
</head>
<body>
    {{ dropzone.load_js() }}
</body>

创建并美化上传区域

如果你不需要对上传区域的样式有太多控制,那么你只需要在想要渲染上传区域的地方使用dropzone.create()方法:

{{ dropzone.create(action='处理上传文件的路由URL') }}

记得把action的值更改成你要处理文件上传的的URL。你可以使用style()方法为上传区域添加简单的自定义样式:

<head>
...  <!-- 在引入Dropzone.js的CSS文件后调用style()方法 -->
{{ dropzone.style('border: 2px dashed #0087F7; margin: 10%') }}
</head>

上传区域的截图示例如下所示:

Flask-Dropzone上传截图

Flask-Dropzone上传截图

在服务器端处理并保存上传文件

当文件被拖拽到上传区域,或是点击上传区域选择上传文件后,这些文件会以AJAX请求的形式发送到你在dropzone.create()方法中使用action参数传入的URL。我们需要在服务器端创建对应的视图函数来处理这些请求,下面是一个最基本的示例:

import os
from flask import Flask, request
from flask_dropzone import Dropzone
app = Flask(__name__)
dropzone = Dropzone(app)
@app.route('/uploads', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':  # 如果请求类型为POST,说明是文件上传请求
        f = request.files.get('file')  # 获取文件对象
        f.save(os.path.join('the/path/to/save', f.filename))  # 保存文件
    return 'upload template'  # 渲染上传页面

上传完成后重定向

这里需要注意的是,因为Dropzone.js通过AJAX请求提交文件,所以你没法在保存文件后将页面重定向。对于这个问题,你可以使用配置变量DROPZONE_REDIRECT_VIEW设置上传完成后跳转到的目标端点,或是添加一个按钮让用户自己点击进行跳转。

服务器端验证

尽管Dropzone.js可以在前端对用户提交的文件进行验证,但为了安全考虑,我们仍然需要在服务器端进行二次验证。在服务器端验证时,如果验证出错,我们不能像往常那样使用flash()函数“闪现”错误消息,因为AJAX请求接受到响应后并不会重载页面,所以不会显示通过flash()函数发送的消息。正确的做法是返回400错误响应,使用错误消息作为响应的主体。下面是一个简单的进行服务器端验证并返回错误消息得示例:

@app.route('/', methods=['POST', 'GET'])
def upload():
    if request.method == 'POST':
        f = request.files.get('file')
        if f.filename.split('.')[1] != 'png':
            return 'PNG only!', 400  # return the error message, with a proper 4XX code
        f.save(os.path.join('the/path/to/save', f.filename))
    return render_template('index.html')

在上面的代码中,我们验证图片是不是png格式,如果不是就返回一个错误提示,在服务器端会在图片下面看到我们返回的错误消息:

validation.png

完整的配置列表

Flask-Dropzone提供了丰富的配置变量,你可以使用它们对Dropzone.js进行各类配置。

Name Default Value Info DROPZONE_SERVE_LOCAL   False       使用Flask-Dropzone提供的load_css()和load_js()方法时,这个变量设置是否使用内置的本地资源,默认从CND加载 DROPZONE_MAX_FILE_SIZE 3 允许的文件最大值,单位为MB DROPZONE_INPUT_NAME file 上传字段的name值 DROPZONE_ALLOWED_FILE_CUSTOM False 是否使用自定义文件类型允许规则,具体见后面 DROPZONE_ALLOWED_FILE_TYPE 'default' 允许的文件类型,具体见后面 DROPZONE_MAX_FILES ‘null’ 一次可以上传的文件数量最大值 DROPZONE_DEFAULT_MESSAGE “Drop files here to upload” 上传区域显示的提示文字 DROPZONE_INVALID_FILE_TYPE “You can’t upload files of this type.” 文件类型错误的错误消息 DROPZONE_FILE_TOO_BIG “File is too big {{filesize}}. Max filesize: {{maxFilesize}}MiB.” 文件太大时显示的错误消息 DROPZONE_SERVER_ERROR “Server error: {{statusCode}}” 服务器错误的错误消息 DROPZONE_BROWSER_UNSUPPORTED “Your browser does not support drag’n’drop file uploads.” 浏览器不支持的错误消息 DROPZONE_MAX_FILE_EXCEED “Your can’t upload any more files.” 超过最大文件数量限制的错误消息 DROPZONE_UPLOAD_MULTIPLE False 是否在单个请求中发送多个文件,默认一个请求发送一个文件 DROPZONE_PARALLEL_UPLOADS 2 当DROPZONE_UPLOAD_MULTIPLE设为True时,设置单个请求包含的文件数量 DROPZONE_REDIRECT_VIEW None 上传完成后重定向的模板端点 DROPZONE_ENABLE_CSRF False 是否开启CSRF保护

这些配置的用法你可以参考Flask-Drozone的文档或是示例程序了解,这里我们仅简单介绍一下对文件类型进行过滤的设置方法。

设置文件类型过滤

Flask-Dropzone内置了一些文件类型(通过MIME定义),可选的值和对应的文件类型如下所示:

  • default:默认值,运行所有类型
  • image:图片
  • audio:音频
  • video:视频
  • text:文本
  • app:程序

你需要为DROPZONE_ALLOWED_FILE_TYPE设置对应的值,比如下面设置仅允许上传图片:

app.config['DROPZONE_ALLOWED_FILE_TYPE'] = 'image'

如果你想要自己定义允许的文件类型列表,那么你需要将DROPZONE_ALLOWED_FILE_CUSTOM设置True,然后传入一个包含允许的文件后缀名列表组成的字符串给DROPZONE_ALLOWED_FILE_TYPE变量,使用逗号分隔多个后缀名,比如:

app.config['DROPZONE_ALLOWED_FILE_CUSTOM'] = True
app.config['DROPZONE_ALLOWED_FILE_TYPE'] = 'image/*, .pdf, .txt'

对于平行上传、CSRF保护等内容的具体实现方法你可以参考文档了解。不过,这个项目目前还没有创建完善的文档,暂时只是写到README里,如果你发现了英文语法或拼写错误,欢迎指正,同时也欢迎为项目贡献代码。

Flask-Dropzone的Git仓库中的examples目录下包含4个示例程序,分别演示了基本用法、CSRF保护、平行上传和上传完成后跳转四个功能。

源码可以在这里找到:https://github.com/greyli/flask-dropzone/tree/master/examples

另外,helloflask仓库里在demos/form目录下的示例程序也包含一个Flask-Dropzone使用示例。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK