前言:本文是学习网易微专业的《python全栈工程师 - Flask高级建站》课程的笔记,欢迎学习交流。同时感谢老师们的精彩传授!
flask-dropzone
,是基于dropzone.js
开发的插件,实现了前端拖拽上传,上传进度显示,上传完成显示的插件,开发人员只需要编写很少的代码就可以完成一个用户体验很好的上传功能。
插件安装:
pipenv install flask-dropzone
配置参数 | 说明 | 默认值 |
---|---|---|
DROPZONE_MAX_FILE_SIZE | 最大文件上传尺寸,单位MB | 3 |
DROPZONE_INPUT_NAME | 上传域name属性值 | file |
DROPZONE_MAX_FILES | 一次最多上传文件数量 | null |
DROPZONE_DEFAULT_MESSAGE | 默认提示 | “Drop files here to upload” |
DROPZONE_FILE_TOO_BIG | 文件尺寸超过限度提示 | “File is too big {{filesize}. Max filesize:{{maxFilessize}}MiB.” |
DROPZONE_SERVER_ERROR | 服务器错误 | “Server error:{{statusCode}}” |
DROPZONE_MAX_FILE_EXCEED | 一次上传文件数量超限 | “You can’t upload any more files.” |
DROPZONE_UPLOAD_MULTIPLE | 禁止一次上传多个文件 | False |
DROPZONE_REDIRECT_VIEW | 上传完成后是否跳转 | False |
DROPZONE_ALLOWED_FILE_TYPE | 允许文件上传类型 | default |
DROPZONE_ALLOWED_FILE_CUSTOM | 自定义允许上传类型 | 自定义允许上传类型 |
DROPZONE_INVALID_FILE_TYPE | 文件类型错误提示 | “You can’t upload files of this type.” |
DROPZONE_ENABLE_CSRF | 是否启用csrf保护 | False |
DROPZONE_TIMEOUT | 上传超时时间,默认30s | None |
DROPZONE_SERVE_LOCAL | 是否加载本地的js插件资源 | False |
允许类型配置参数值 | 默认值 |
---|---|
default | image/*, audio/*, video/*, text/*, application/* |
image | image/* |
audio | audio/* |
video | video/* |
text | text/* |
app | application/* |
# libs.py:
from flask_dropzone import Dropzone
dropzone = Dropzone()
# app.py:
dropzone.init_app(app)
实操:
Step1
:修改settings.py
文件,增加flask-dropzone
配置
.
.
.
CKEDITOR_FILE_BROWSER = "upload.ckeditor_browser"
# 新增下面的配置++
# Dropzone配置
DROPZONE_MAX_FILE_SIZE = 3
DROPZONE_MAX_FILES = 30
DROPZONE_ALLOWED_FILE_CUSTOM = True
DROPZONE_ALLOWED_FILE_TYPE = 'image, .jpg, .pdf, .txt'
DROPZONE_ENABLE_CSRF = True
# 上传域名字为upload
DROPZONE_INPUT_NAME = 'upload'
.
.
.
Step2
:修改libs.py
文件:导入flask-dropzone
并实例化
from flask import session, redirect, url_for
from functools import wraps
from flask_wtf.csrf import CSRFProtect
from flask_ckeditor import CKEditor
# 新增下面这一行
from flask_dropzone import Dropzone
# 创建数据库对象
# 这里不用传入app实例对象,因为这里尚未创建app实例
db=SQLAlchemy()
csrf = CSRFProtect()
ckeditor = CKEditor()
# 新增下面这一行
dropzone = Dropzone()
.
.
.
Step3
:修改app.py
文件,导入flask-dropzone
并初始化
from flask import Flask, render_template
from flask import request, redirect, url_for
# 新增下面这一行: 导入dropzone
from libs import db, ckeditor, csrf, dropzone
.
.
.
db.init_app(app)
ckeditor.init_app(app)
csrf.init_app(app)
# 新增下面这一行:初始化dropzone
dropzone.init_app(app)
.
.
.
flask-dropzone
插件与flask-ckeditor
一样,都是基于前端的一些框架开发的后端插件,最后要在模版生成前端插件
可以使用下面代码,加载静态的js
与css
资源
{{ dropzone.load_css() }}
{{ dropzone.load_js() }}
{{ dropzone.style() }} // 自定义样式
可以Dropzone
官网下载相应的静态资源:
官网地址:dropzonejs.com
官方推荐使用远程CDN
资源
在需要创建上传的区域:
{{ dropzone.create(action="upload.dropzone_upload")}}
使用:{{ dropzone.config()}}
加载配置
在视图函数中,与flask-ckeditor
一样,上传完成后,需要返回数据,不同的是,自己要在前端定义ajax
回调函数,事件配置:https://www.dropzonejs.com/#event-success
dropzone.config(custom_init='this.on("success", function(file, res) {
if (file.xhr.status == 200) {
$('#thumb').val(res.file)
} else {
alert('上传失败')
}
}')
如果希望传递json
数据,使用jquery
时,jquery
会根据设置,自动将后台返回的json
串转换为json
对象,但是dropzone
还需要根据响应头来确定是否转换为json
对象,使用json
模块,只是将数据转换为了json
串,并不能输出正确的响应头。
在css
部分插入:
{{ dropzone.load_css() }}
在js
部分插入:
{{ dropzone.load_js() }}
插入回调上传后回调处理:
{{ dropzone.config(custom_init(...)) }}
实操:
Step1
:修改models.py
文件,在Article()
模型中添加thumb
字段,记得更新数据库(开发环境下,如果更新出错,可以删除数据库重新建库。生产环境下更新出错就不要这样做了!!)
.
.
.
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
# 新增下面这一行
thumb = db.Column(db.String)
intro = db.Column(db.String)
content = db.Column(db.Text)
author = db.Column(db.String)
pubdate = db.Column(db.DateTime, default=datetime.utcnow)
cate_id = db.Column(db.Integer, db.ForeignKey("category.cate_id"))
.
.
.
Step2
:
修改templates/memeber/article/artilce_post.html
内容:
.
.
.
{% block content %}
{{ dropzone.load_css() }}
{{ dropzone.load_js() }}
<div class="container-fluid">
<div class="row">
<div class="main col-md-12 col-lg-12 col-xs-12 col-sm-12" >
<h3>文章发布h3>
<div class="alert alert-info" id="message" role="alert">
.
.
.
<div class="form-group">
<label for="name">标题:label>
<input class="form-control" type="text" name="title" id="title" value="" placeholder="文章标题">
{{ dropzone.create(action="upload.ckeditor_upload") }}
<input type="hidden" name="thumb" id="thumb">
div>
.
.
.
div>
div>
div>
{# 新增下面的dropzone配置 #}
{# 这里一定要将后台的上传视图返回的数据使用flask.jsonify处理 #}
{{ dropzone.config(
custom_init='this.on("success", function(file, res) {
if( file.xhr.status == 200 ){
$("#thumb").val(res.url)
console.log(res)
}else{
alert("上传失败");
}
});'
)
}}
<script src="{{ url_for('static', filename='ckeditor/ckeditor.js') }}">script>
{# 新增下面两个js脚本 #}
<script>
CKEDITOR.replace( 'content',{
filebrowserUploadUrl :'/upload/ckeditor',
filebrowserBrowseUrl:'/upload/ckeditor/browser',
//CSRF启用后,需要按照CSRF要求传递CSRF_TOKEN
fileTools_requestHeaders :{
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': '{{ csrf_token() }}'
}
})
script>
<script>
btn = document.getElementById("btn1")
btn.onclick = function (ev) {
cate_id = $("#cate").val()
title = $("#title").val()
thumb = $("#thumb").val()
intro = $("#intro").val()
content = CKEDITOR.instances["content"].getData()
if (title == ""){
$("#title").focus()
return false
}
if (intro == ""){
$("#intro").focus()
return false
}
$.ajax({
type:"post",
data:{"cate":cate_id,
"title":title,
"thumb":thumb,
"intro":intro,
"content":content
},
dataType:"json",
beforeSend:function(xhr){
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token() }}")
},
success: function(data){
$("#message").html(data.message)
//表单重置,相当于点击重设按钮
$("#title").val("")
$("#intro").val()
// 清空隐藏域
$("#thumb").val("")
// 清空上传
myDropzone.dropzone.removeAllFiles()
// 清空ckeditor
CKEDITOR.instances["content"].setData()
}
})
}
script>
{% endblock %}
Step3
:修改templates/index.html
文件
.
.
.
<div class="col-md-6">
<ul class="media-list">
{% for article in articles %}
<li class="media">
<a href="" class="pull-left">
<img src="{{ article.thumb }}" class="media-object">
a>
<div class="media-body">
<h4 class="media-heading">
<a href="{{ url_for('article_app.view', article_id=article.id) }}">
{{ article.title }}
a>
h4>
<div class="media">
<p>{{ article['intro'] }}p>
div>
div>
li>
{% endfor %}
ul>
<ul class="pagination">
{% for page in pageList %}
{% if page!=None %}
<li>
<a href="{{ url_for('article_app.getArticleList', page=page) }}">{{ page }}a>
li>
{% else %}
<li><a href="#">...a>li>
{% endif %}
{% endfor %}
ul>
div>
<div class="col-md-4">
.
.
.
1.忘记修改模型,添加模型字段
2.忘记升级数据库(flask db migrate,flask db update
)
3.忘记添加ajax
发送数据中添加csrf
请求头
4.忘记ajax
提交数据时,添加上标题图片文件地址数据(隐藏域)
5.忘记在后台视图函数中添加接收标题图数据(来自隐藏域)
6.需要在前台列表模版中,添加标题图标签
flask-dropzone配置
dropzone
模版渲染