[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)

大家都在看

  • 01-MySQL连接查询、聚合函数

    1、连接查询 1.1、左连接 以左表为基准进行查询,左表数据回全部显示出来 右表中如果匹配连接条件的数据则显示相应字段的数据,如果不匹配,则显示为NULL 1.2、右连接 以右表为…

    Linux 2023年6月7日
    0128
  • 基于LNMP快速简单搭建wordpress平台

    一、WordPress 简介 WordPress是一种使用PHP语言开发的博客平台,用户可以在支持PHP和MySQL数据库的服务器上架设属于自己的网站。也可以把WordPress当…

    Linux 2023年6月7日
    0105
  • 解决nginx反向代理Mixed Content和Blockable问题

    bash;gutter:false; nginx配置https反向代理,按F12发现js等文件出现Mixed Content,Optionally-blockable 和 Bloc…

    Linux 2023年6月7日
    0141
  • 用go把博客园博客下载到本地Hexo目录下

    找到cookie 直接浏览器F12 巴拉巴拉 直接上代码 用hexo建静态博客的话,go文件在 \source\_posts目录下,run之后将会在此目录下生成cnblogs文件夹…

    Linux 2023年6月7日
    095
  • WPF 已知问题 某些设备上的应用在 WindowChromeWorker 抛出 System.OverflowException 异常

    准确来说,这个不算是 WPF 的问题,而是系统等的问题。在某些设备上的使用了 WindowChrome 功能的 WPF 应用,将在运行过程,在 WindowChromeWorker…

    Linux 2023年6月6日
    0100
  • 一劳永逸,解决.NET发布云服务器的时区问题

    国内大多数开发者使用的电脑,都是使用的北京时间,日常开发的过程中其实并没有什么不便;不过,等遇到了阿里云等云服务器,系统默认使用的时间大多为 UTC时间,这个时候,时区和时间的问题…

    Linux 2023年6月6日
    097
  • [随记]-linux侦听端口的4种方法

    侦听 192.168.0.1 服务器上的 10086 端口是否打开 1. telnet telnet是windows 内置的功能,当然 linux 也有。用法: tenlet 19…

    Linux 2023年6月6日
    0110
  • Action Sheets 和 Activity Views

    行动表行动表是一种特殊的提醒方式,它展示了与当前环境相关的两个或多个选择。在较小的屏幕上,动作表会从屏幕的底部滑上去;在较大的屏幕上,动作表会以弹出式的方式一次性出现。 在执行一个…

    Linux 2023年6月7日
    0100
  • Jenkins,8080端口无法修改问题

    今天安装了 Jenkins 。想把8080 端口修改为 6699 。 出现以下状况。 一:找到 jenkins.xml 文件。 vi /usr/lib/firewalld/serv…

    Linux 2023年6月13日
    089
  • Markdown基本使用

    元素 Markdown 语法 [标题(Heading)] [粗体(Bold)] [斜体(Italic)] [引用块(Blockquote)] [有序列表(Ordered List)…

    Linux 2023年6月13日
    092
  • CentOS 7上安装和配置Ghost

    Ghost是一个轻量级的开源博客平台,易于使用。Ghost是完全可定制的,有许多主题可用。 在本教程中,您将在CentOS 7上设置Ghost。您还将配置Nginx以代理对Ghos…

    Linux 2023年6月13日
    071
  • Xshell配置ssh免密码登录-密钥公钥(Public key)与私钥(Private Key)登录【已成功实例】

    本文转自https://blog.csdn.net/qjc_501165091/article/details/51278696 ssh登录提供两种认证方式:口令(密码)认证方式和…

    Linux 2023年5月28日
    088
  • 不可不知的软件架构模式

    什么是系统架构(Architecture) 设计不仅仅指的是外观和感觉,它还包括运作方式。—— 史蒂夫·乔布斯 系统架构(System Architecture),软件架构(Sof…

    Linux 2023年6月14日
    079
  • 软件测试基础理论

    软件基础的理论 一, 什么是软件产品 它是一个逻辑产品,没有实体,包括程序,文档和数据,需要通过终端设备才能体现出来功能和作用 二, 软件产品的中间过程文档 客户需求 &#…

    Linux 2023年6月7日
    088
  • 自己写的文件夹图标修改脚本

    自己写了一个文件图标修改的Python脚本,只要把文件夹拖动到这个脚本上,就可以用文件夹中的图片和视频作为文件夹的封面。把图片或视频拖到脚本上,就可以把这个图片或视频用作其所在文件…

    Linux 2023年6月6日
    0165
  • python学习

    python中的字符串以双引号或者单引号表示 长度为L:第一个字节索引为0或-L 最后一个字节索引为L-1或-1 in是二元关系操作,用来判断左侧内容是否在右侧的集合中 float…

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