使用Flask-Dropzone在Flask程序中实现文件上传

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


Dropzone.js是一个提供文件上传、验证、预览、上传进度条等功能的JavaScript库。Flask-Dropzone在模板中提供了一些方法来帮助你创建上传区域,引入相关资源。你只需要添加一些配置就可以实现上传类型的过滤,文件大小限制,上传后跳转等功能。当然,你还要自己编写视图函数来处理和保存文件,并进行服务器端的二次验证。如果你不熟悉服务器端的上传文件处理,可以考虑浏览一下这篇 《Flask文件上传(一):原生实现》
《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。你可以使用dropzone.style()方法为上传区域添加简单的自定义样式:
<head>
...  <!-- 在引入Dropzone.js的CSS文件后调用style()方法 -->
{{ dropzone.style('border: 2px dashed #0087F7; margin: 10%') }}
</head>复制代码
上传区域的截图示例如下所示:

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

当文件被拖拽到上传区域,或是点击上传区域选择上传文件后,这些文件会以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格式,如果不是就返回一个错误提示,在服务器端会在图片下面看到我们返回的错误消息:

完整的配置列表

Flask-Dropzone提供了丰富的配置变量,你可以使用它们对Dropzone.js进行各类配置。很遗憾掘金的编辑器不支持表格,所以这里只能插入一张图片,如果你需要文本,可以访问 这篇文章

这些配置的用法你可以参考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保护、平行上传和上传完成后跳转四个功能。
另外, helloflask仓库里在demos/form目录下的示例程序也包含一个Flask-Dropzone使用示例。

相关链接


 类似资料: