[python][flask] Flask 图片上传与下载例子(支持漂亮的拖拽上传)

1、效果预览

我们基于 Flask 官方指导工程,增加一个图片拖拽上传功能,效果如下:

[python][flask] Flask 图片上传与下载例子(支持漂亮的拖拽上传)

2、新增逻辑概览

我们在官方指导工程 https://github.com/pallets/flask/tree/2.1.1/examples/tutorial/flaskr 上进行增加代码,改动如下:

➜  flaskr git:(main) ✗ tree
.
├── static
│   ├── file
│   │   ├── css
&#x2502;&#xA0;&#xA0; &#x2502;&#xA0;&#xA0; &#x2502;&#xA0;&#xA0; &#x2514;&#x2500;&#x2500; 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 加入:

[python][flask] Flask 图片上传与下载例子(支持漂亮的拖拽上传)
  • 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 的指导项目之后,实现一个图片上传和下载的案例…

如果觉得不错,帮忙点个支持哈~**

[python][flask] Flask 图片上传与下载例子(支持漂亮的拖拽上传)

Original: https://www.cnblogs.com/zjutlitao/p/16325464.html
Author: beautifulzzzz
Title: [python][flask] Flask 图片上传与下载例子(支持漂亮的拖拽上传)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585653/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • python_列表

    列表创建 列表类似数组,但是以一对方括号 []作为符号,同时支持-1到-n的下标访问。列表中包含的是元素值的引用 并且,列表中的元素可以不是同一类型的 列表的创建: listNew…

    Linux 2023年6月7日
    0133
  • 真香警告!JitPack 开源库集成平台

    前言: 请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i 简介 官方介绍: JitPack 是一个用于 JVM 和 Android 项目的新颖的包存储库。它按需构建 Git…

    Linux 2023年6月14日
    0106
  • 网络扫描(一)

    使用工具:Kali Linux、Metaspoliatable(作为攻击目标) 扫描的4个不同阶段 用ping验证系统是否正在运行。 用Nmap扫描目标主机的端口。 用Nmap脚本…

    Linux 2023年6月14日
    0102
  • 利用 C# 给 Windows 资源管理器注册右键菜单(Windows Shell)(一):入门

    前言 关于 SharpShell SharpShell makes it easy to create Windows Shell Extensions using the .NE…

    Linux 2023年5月28日
    084
  • Linux的文件结构和基本分类

    (注:此笔记默认操作系统为centOS7) Windows下的文件都是以盘符开头的,Linux中没有盘符,所有文件都在根目录下。 cd /进入根目录 ls /显示根目录下的文件查看…

    Linux 2023年6月7日
    087
  • 2021年3月-第02阶段-前端基础-Flex 伸缩布局-移动WEB开发_流式布局

    移动web开发流式布局 1.0 移动端基础 1.1 浏览器现状 PC端常见浏览器:360浏览器、谷歌浏览器、火狐浏览器、QQ浏览器、百度浏览器、搜狗浏览器、IE浏览器。 移动端常见…

    Linux 2023年6月8日
    0115
  • shell handle

    !/bin/bash qinrui set -e commitId =” repoPath =” x1 =” if [-f changes15….

    Linux 2023年5月28日
    0103
  • 接口压测提示redis获取不到连接数,出现timeout waiting for idle object异常问题定位

    博客园 :当前访问的博文已被密码保护 请输入阅读密码: Original: https://www.cnblogs.com/qmfsun/p/11583355.htmlAuthor…

    Linux 2023年5月28日
    080
  • HTML笔记整理–上节

    一、认识WEB 「网页」主要是由 &#x6587;&#x5B57;、 &#x56FE;&#x50CF;和 &#x8D85;&#x94…

    Linux 2023年6月13日
    089
  • docker使用

    1 docker介绍,跟传统虚拟机的比较 2 docker架构图 3 docker安装 3.1 windows安装 3.2 乌班图 3.3 centos上安装(必须7.0以上) 3…

    Linux 2023年6月14日
    076
  • java内存调优总结

    ···bashJVM 调优,是个很简单也很复杂的话题,由于经常遇到这类问题,在这里总结一下。 先从解决bug开始,当Java程序申请内存,超出VM可分配内纯的时候,VM首先可能会G…

    Linux 2023年6月14日
    083
  • 手把手搭建一个属于自己的在线 IDE

    背景 这几个月在公司内做一个跨前端项目之间共享组件/区块的工程,主要思路就是在 Bit 的基础上进行开发。Bit 主要目的是实现不同项目 共享 与 同步 组件/区块,大致思路如下:…

    Linux 2023年6月14日
    0101
  • nginx-openresty通过location调用显示upstream信息

    背景 有时候查看nginx的upstream配置得知配置后端的ip地址和端口,但从日志里面发现提示后端不存在,想知道nginx的内存里面是否存在upstream的加载信息,判断后端…

    Linux 2023年6月6日
    0117
  • MSSQL中完整备份及完整还原的T-SQL实践

    | 0.37分钟 | 596.8字符 | 1、引言&背景 2、完整备份 3、完整还原 4、声明与参考资料 | SCscHero | 2022/5/27 AM12:47 | …

    Linux 2023年6月14日
    074
  • npm常用命令

    npm install npm install=npm i。在git clone项目的时候,项目文件中并没有 node_modules文件夹,项目的依赖文件可能很大。直接执行,np…

    Linux 2023年6月13日
    097
  • Putty&Psftp命令行实现自动登录

    | 0.18分钟 | 292.8字符 | 1、引言&背景 2、解决方案 3、声明与参考资料 | SCscHero | 2022/1/22 PM6:0 | 系列 | 已完成 …

    Linux 2023年5月27日
    088
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球