- 1、效果预览
- 2、新增逻辑概览
- 3、tuchuang.py 逻辑介绍
- 3.1 图片上传
- 3.2 图片合法检查
- 3.3 图片下载
- 4、init.py 逻辑介绍
- 5、upload.html 介绍
- 5.1 upload Jinja 模板介绍
- 5.2 upload css 介绍(虚线框)
- 5.3 upload js 介绍(拖拽)
- 6、后记
- 参考链接
1、效果预览
我们基于 Flask 官方指导工程,增加一个图片拖拽上传功能,效果如下:
2、新增逻辑概览
我们在官方指导工程 https://github.com/pallets/flask/tree/2.1.1/examples/tutorial/flaskr 上进行增加代码,改动如下:
➜ flaskr git:(main) ✗ tree
.
├── static
│   ├── file
│   │   ├── css
│   │   │   └── upload.css <- 9 18 增加图片上传的 css │ ├── img 20220525004341_22.png └── 20220529231518_76.png js upload.js <- style.css templates auth login.html register.html base.html blog create.html index.html update.html tuchuang html upload.html auth.py blog.py db.py __init__.py schema.sql tuchuang.py 增加图床 python 蓝图 directories, files < code></->
由于 flask 官方 Demo 基于蓝图设计,这给我们新增逻辑带来了很大的方便。关于官方 Demo 的介绍,可以参考我的《Flask 入门(以一个博客后台为例)》
3、tuchuang.py 逻辑介绍
3.1 图片上传
1)该接口采用 POST 方法,需要登录;
2)接着,检查请求中是否有 ‘file’ 关键词,然后取出文件,判断文件是否为空或是否合法;
3)最后,将上传的图片保存(采用秒级别的时间戳+随机数重命名);
4)该接口在上传图片成功后,返回该图片的链接;如果不成功,返回 upload.html 页面;
@bp.route('/', methods=['GET', 'POST'])
@login_required
def upload_file():
if request.method == 'POST':
# check if the post request has the file part
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
# If the user does not select a file, the browser submits an
# empty file without a filename.
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
# 获取安全的文件名 正常文件名
filename = secure_filename(file.filename)
# 生成随机数
random_num = random.randint(0, 100)
# f.filename.rsplit('.', 1)[1] 获取文件的后缀
filename = datetime.now().strftime("%Y%m%d%H%M%S") + "_" + str(random_num) + "." + filename.rsplit('.', 1)[1]
file_path = app.config['UPLOAD_FOLDER'] # basedir 代表获取当前位置的绝对路径
# 如果文件夹不存在,就创建文件夹
if not os.path.exists(file_path):
os.makedirs(file_path)
file.save(os.path.join(file_path, filename))
return redirect(url_for('tuchuang.download_file', name=filename))
return render_template("tuchuang/upload.html")
3.2 图片合法检查
上述代码中有一个合法检测的函数 allowed_file
,用于检查上传图片的后缀是否在允许列表:
basedir = os.path.abspath(os.path.dirname(__file__)) # 获取当前文件所在目录
UPLOAD_FOLDER = basedir+'/static/file/img' # 计算图片文件存放目录
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} # 设置可上传图片后缀
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
bp = Blueprint("tuchuang", __name__, url_prefix="/tuchuang")
def allowed_file(filename): # 检查上传图片是否在可上传图片允许列表
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
3.3 图片下载
图片下载比较简单,就是调用 send_from_directory
函数,就能够把 static 目录下的对应文件发出:(我们一般把各种用于外面访问的静态图片、JS、CSS 等放在 static 文件中)
@bp.route('/download/')
def download_file(name):
return send_from_directory(app.config["UPLOAD_FOLDER"], name)
4、init.py 逻辑介绍
由于我们采用蓝图设计,因此需要稍微修改下 __init__.py
文件,来将 tuchuang.py 加入:
MAX_CONTENT_LENGTH=16 * 1000 * 1000
上传图片大小限制from flaskr import auth, blog, tuchuang
app.register_blueprint(tuchuang.bp)
将 tuchuang 加入蓝图app.add_url_rule("/download/<name>", endpoint="download_file", build_only=True)</name>
5、upload.html 介绍
5.1 upload Jinja 模板介绍
- Jinja 引用外部 css:
<link rel="stylesheet" href="{{ url_for('static', filename='file/css/upload.css') }}">
- Jinja 引用外部 js:
<script type="text/javascript" src="{{ url_for('static', filename='file/js/upload.js') }}"></script>
- 该 Jinja 模板实现了两种图片上传交互:
- 普通版,采用 file select 框 + submit 按钮,实现图片上传:
- 拖拽版(需要借助 JS,CSS),在 内实现
下面是 tuchuang/upload.html
完整代码:
Upload new File
Upload new File
Upload multiple files with the file dialog or by dragging and dropping images onto the dashed region
Select some files
5.2 upload css 介绍(虚线框)
下面是拖拽需要用到的 CSS,大家暂时浏览下,之后结合 JS 就明白了:
➜ css git:(main) ✗ cat upload.css
#drop-area {
border: 2px dashed #ccc;
border-radius: 20px;
width: 480px;
font-family: sans-serif;
margin: 100px auto;
padding: 20px;
}
#drop-area.highlight {
border-color: purple;
}
p {
margin-top: 0;
}
.my-form {
margin-bottom: 10px;
}
#gallery {
margin-top: 10px;
}
#gallery img {
width: 150px;
margin-bottom: 10px;
margin-right: 10px;
vertical-align: middle;
}
.button {
display: inline-block;
padding: 10px;
background: #ccc;
cursor: pointer;
border-radius: 5px;
border: 1px solid #ccc;
}
.button:hover {
background: #ddd;
}
#fileElem {
display: none;
}
5.3 upload js 介绍(拖拽)
5.3.1 JS 拖拽框架
JS 代码主要基于 window.onload + 拖拽事件实现,大致框架如下:
➜ js git:(main) ✗ cat upload.js
window.onload=function(){
var dropArea = document.getElementById('drop-area')
// 阻止默认行为
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false)
})
function preventDefaults (e) {
e.preventDefault()
e.stopPropagation()
}
// 增加事件,鼠标拖入边框高亮,拖出边框变为原来样子
;['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false)
})
;['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false)
})
function highlight(e) {
dropArea.classList.add('highlight')
}
function unhighlight(e) {
dropArea.classList.remove('highlight')
}
// 增加事件,鼠标放下,之后准备上传图片
dropArea.addEventListener('drop', handleDrop, false)
function handleDrop(e) {
// 之后准备上传图片
}
}
window.onload() 方法用于在网页加载完毕后立刻执行的操作,即当 HTML 文档加载完毕后,立刻执行某个方法。
为什么使用 window.onload()?
因为 JavaScript 中的函数方法需要在 HTML 文档渲染完成后才可以使用,如果没有渲染完成,此时的 DOM 树是不完整的,这样在调用一些 JavaScript 代码时就可能报出”undefined”错误。
5.3.2 JS 图片上传
function handleDrop(e) {
// 从拖拽放下事件中获取拖拽的文件
let dt = e.dataTransfer
let files = dt.files
// 调用图片处理函数,对图片进行处理
handleFiles(files)
}
function handleFiles(files) {
// 对于多个图片,循环调用 uploadFile 函数,进行上传
([...files]).forEach(uploadFile)
}
function uploadFile(file) {
// JS 合成表单,利用 POST 方法,实现上传(部署在远端时,要改下下面的 url)
let url = 'http://127.0.0.1:5000/tuchuang/'
let formData = new FormData()
formData.append('file', file)
fetch(url, {
method: 'POST',
body: formData
})
.then(progressDone) // { /* Error. Inform the user */ })
}
Fetch API 提供了一个 JavaScript接口,用于访问和操纵HTTP管道的部分,例如请求和响应。它还提供了一个全局 fetch()方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。详细介绍参考《参考链接[8]》:
- 1.进行 fetch 请求 参考;
- 2.支持的请求参数参考;
- 3.发送带凭据的请求参考;
- 4.上传 JSON 数据参考;
- 5.上传文件参考;
- 6.上传多个文件参考;
- 7.检测请求是否成功参考;
- 8.自定义请求对象参考;
- 9.Headers参考;
- 10.Guard参考;
- 11.Response 对象参考;
- 12.Body参考;
- 13.特性检测参考;
该文章讲的比较好,大家可以跳转过去学习下~
5.3.3 JS 图片上传进度条
想要带有进度条,我们需要修改下 handleFiles 函数:
var filesDone = 0
var filesToDo = 0
var progressBar = document.getElementById('progress-bar')
...
// 预览
function previewFile(file) {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = function() {
let img = document.createElement('img')
img.src = reader.result
document.getElementById('gallery').appendChild(img)
}
}
// 进度条初始化,fileDone 置 0,filesToDo 置需要上传图片总数
function initializeProgress(numfiles) {
progressBar.value = 0
filesDone = 0
filesToDo = numfiles
}
// 注意,该函作为 fetch 的返回回调函数,意思是每次传输完成一个图片,进度条进行相应变化
function progressDone() {
filesDone++
progressBar.value = filesDone / filesToDo * 100
}
function handleFiles(files) {
files = [...files]
initializeProgress(files.length)
files.forEach(uploadFile)
files.forEach(previewFile)
}
6、后记
本文涉及到的源代码在 GITHUB,后续我会基于该工程加入各种有意思的功能。
此外,之前的两篇文章列在下面,可能对您理解本文有帮助:
参考链接
[1]. 本文代码 GITHUB
[2]. 在HTML中引入CSS的几种方式介绍
[3]. python Flask中html模版中如何引用css,js等资源
[4]. HTML引入JS的两种方法
[5]. 使用Flask引用HTML中的.js文件的静态资源问题
[6]. Flask 官方指导 Uploading Files
[7]. JavaScript window.onload
[8]. JavaScript使用 Fetch
[9]. 本文 JS+CSS 参考
: **这篇是在大家熟悉 flaskr 的指导项目之后,实现一个图片上传和下载的案例…
如果觉得不错,帮忙点个支持哈~**
Original: https://www.cnblogs.com/zjutlitao/p/16325464.html
Author: beautifulzzzz
Title: [python][flask] Flask 图片上传与下载例子(支持漂亮的拖拽上传)
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585653/
转载文章受原作者版权保护。转载请注明原作者出处!