本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes

1 简介

   这是我的系列教程Python+Dash快速web应用开发的第十一期,在之前两期的教程内容中,我们掌握了在Dash中创建完善的表单控件的方法。

  而在今天的教程中,我们将介绍如何在Dash中高效地开发web应用中非常重要的文件上传下载功能。

图1

2 在Dash中实现文件上传与下载

2.1 在Dash中配合dash-uploader实现文件上传

  其实在自带的dash_core_components中就封装了基于html5原生API的dcc.Upload()组件,可以实现简单的文件上传功能,但说实话,非常的不好用,其主要缺点有:

  • 文件大小有限制,150M到200M左右即出现瓶颈
  • 策略是先将用户上传的文件存放在浏览器内存,再通过base64形式传递到服务端再次解码,非常低效
  • 整个上传过程无法配合准确的进度条

  正是因为Dash自带的上传部件如此不堪,所以一些优秀的第三方拓展涌现出来,其中最好用的要数dash-uploader,它解决了上面提到的dcc.Upload()的所有短板。通过pip install dash-uploader进行安装之后,就可以直接在Dash应用中使用了。

  我们先从极简的一个例子出发,看一看在Dash中使用dash-uploader的正确姿势:

app1.py

import dash
import dash_uploader as du
import dash_bootstrap_components as dbc
import dash_html_components as html app = dash.Dash(__name__) # 配置上传文件夹
du.configure_upload(app, folder='temp') app.layout = html.Div(
dbc.Container(
du.Upload()
)
) if __name__ == '__main__':
app.run_server(debug=True)

图2

  可以看到,仅仅十几行代码,我们就配合dash-uploader实现了简单的文件上传功能,其中涉及到dash-uploader两个必不可少的部分:

2.1.1 利用du.configure_upload()进行配置

  要在Dash中正常使用dash-uploader,我们首先需要利用du.configure_upload()进行相关配置,其主要参数有:

  app,即对应已经实例化的Dash对象;

  folder,用于设置上传的文件所保存的根目录,可以是相对路径,也可以是绝对路径;

  use_upload_id,bool型,默认为True,这时被用户上传的文件不会直接置于folder参数指定目录,而是会存放于du.Upload()部件的upload_id对应的子文件夹之下;设置为False时则会直接存放在根目录,当然没有特殊需求还是不要设置为False。

  通过du.configure_upload()我们就完成了基本的配置。

2.1.2 利用du.Upload()创建上传部件

  接下来我们就可以使用到du.Upload()来创建在浏览器中渲染供用户使用的上传部件了,它跟常规的Dash部件一样具有id参数,也有一些其他的丰富的参数供开发者充分自由地自定义功能和样式:

  text,字符型,用于设置上传部件内显示的文字;

  text_completed,字符型,用于设置上传完成后显示的文字内容前缀;

  cancel_button,bool型,用于设置是否在上传过程中显示“取消”按钮;

  pause_button,bool型,用于设置是否在上传过程中显示“暂停”按钮;

  filetypes,用于限制用户上传文件的格式范围,譬如['zip', 'rar', '7zp']就限制用户只能上传这三种格式的文件。默认为None即无限制;

  max_file_size,int型,单位MB,用于限制单次上传的大小上限,默认为1024即1GB;

  default_style,类似常规Dash部件的style参数,用于传入css键值对,对部件的样式进行自定义;

  upload_id,用于设置部件的唯一id信息作为du.configure_upload()中所设置的缓存根目录的下级子目录,用于存放上传的文件,默认为None,会在Dash应用启动时自动生成一个随机值;

  max_files,int型,用于设置一次上传最多可包含的文件数量,默认为1,也推荐设置为1,因为目前对于多文件上传仍有进度条异常上传结束显示异常等bug,所以不推荐设置大于1。

  知晓了这些参数的作用之后,我们就可以创建出更符合自己需求的上传部件:

app2.py

import dash
import dash_uploader as du
import dash_bootstrap_components as dbc
import dash_html_components as html app = dash.Dash(__name__) # 配置上传文件夹
du.configure_upload(app, folder='temp') app.layout = html.Div(
dbc.Container(
du.Upload(
id='uploader',
text='点击或拖动文件到此进行上传!',
text_completed='已完成上传文件:',
cancel_button=True,
pause_button=True,
filetypes=['md', 'mp4'],
default_style={
'background-color': '#fafafa',
'font-weight': 'bold'
},
upload_id='我的上传'
)
)
) if __name__ == '__main__':
app.run_server(debug=True)

图3

  但像前面的例子那样直接在定义app.layout时就传入实际的du.Upload()部件,会产生一个问题——应用启动后,任何访问应用的用户都对应一样的upload_id,这显然不是我们期望的,因为不同用户的上传文件会混在一起。

  因此可以参考下面例子的方式,在每位用户访问时再渲染随机id的上传部件,从而确保唯一性:

app3.py

import dash
import dash_uploader as du
import dash_bootstrap_components as dbc
import dash_html_components as html import uuid app = dash.Dash(__name__) # 配置上传文件夹
du.configure_upload(app, folder='temp') def render_random_id_uploader(): return du.Upload(
id='uploader',
text='点击或拖动文件到此进行上传!',
text_completed='已完成上传文件:',
cancel_button=True,
pause_button=True,
filetypes=['md', 'mp4'],
default_style={
'background-color': '#fafafa',
'font-weight': 'bold'
},
upload_id=uuid.uuid1()
) def render_layout(): return html.Div(
dbc.Container(
render_random_id_uploader()
)
) app.layout = render_layout if __name__ == '__main__':
app.run_server(debug=True)

  可以看到,每次访问时由于upload_id不同,因此不同的会话拥有了不同的子目录。

图4

2.1.3 配合du.Upload()进行回调

  在du.Upload()中额外还有isCompletedfileNames两个属性,前者用于判断当前文件是否上传完成,后者则对应此次上传的文件名称,参考下面这个简单的例子:

app4.py

import dash
import dash_uploader as du
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash.dependencies import Input, Output, State app = dash.Dash(__name__) # 配置上传文件夹
du.configure_upload(app, folder='temp') app.layout = html.Div(
dbc.Container(
[
du.Upload(id='uploader'),
html.H5('上传中或还未上传文件!', id='upload_status')
]
)
) @app.callback(
Output('upload_status', 'children'),
Input('uploader', 'isCompleted'),
State('uploader', 'fileNames')
)
def show_upload_status(isCompleted, fileNames):
if isCompleted:
return '已完成上传:'+fileNames[0] return dash.no_update if __name__ == '__main__':
app.run_server(debug=True, port=8051)

图5

2.2 配合flask进行文件下载

  相较于文件上传,在Dash中进行文件的下载就简单得多,因为我们可以配合flasksend_from_directory以及html.A()部件来为指定的服务器端文件创建下载链接,譬如下面的简单示例就打通了文件的上传与下载:

app5.py

from flask import send_from_directory
import dash
import dash_uploader as du
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
import os app = dash.Dash(__name__) du.configure_upload(app, 'temp', use_upload_id=False) app.layout = html.Div(
dbc.Container(
[
du.Upload(id='upload'),
html.Div(
id='download-files'
)
]
)
) @app.server.route('/download/<file>')
def download(file): return send_from_directory('temp', file) @app.callback(
Output('download-files', 'children'),
Input('upload', 'isCompleted')
)
def render_download_url(isCompleted): if isCompleted:
return html.Ul(
[
html.Li(html.A(f'/{file}', href=f'/download/{file}', target='_blank'))
for file in os.listdir('temp')
]
) return dash.no_update if __name__ == '__main__':
app.run_server(debug=True)

图6

3 用Dash编写简易个人网盘应用

  在学习了今天的案例之后,我们就掌握了如何在Dash中开发文件上传及下载功能,下面我们按照惯例,结合今天的主要内容,来编写一个实际的案例;

  今天我们要编写的是一个简单的个人网盘应用,我们可以通过浏览器访问它,进行文件的上传、下载以及删除:

图7

app6.py

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import dash_uploader as du
import os
from flask import send_from_directory
import time app = dash.Dash(__name__, suppress_callback_exceptions=True) du.configure_upload(app, 'NetDisk', use_upload_id=False) app.layout = html.Div(
dbc.Container(
[
html.H3('简易的个人云盘应用'),
html.Hr(),
html.P('文件上传区:'),
du.Upload(id='upload',
text='点击或拖动文件到此进行上传!',
text_completed='已完成上传文件:',
max_files=1000),
html.Hr(),
dbc.Row(
[
dbc.Button('删除选中的文件', id='delete-btn', outline=True),
dbc.Button('打包下载选中的文件', id='download-btn', outline=True)
]
),
html.Hr(),
dbc.Spinner(
dbc.Checklist(
id='file-list-check'
)
),
html.A(id='download-url', target='_blank')
]
)
) @app.server.route('/download/<file>')
def download(file):
return send_from_directory('NetDisk', file) @app.callback(
[Output('file-list-check', 'options'),
Output('download-url', 'children'),
Output('download-url', 'href')],
[Input('upload', 'isCompleted'),
Input('delete-btn', 'n_clicks'),
Input('download-btn', 'n_clicks')],
State('file-list-check', 'value')
)
def render_file_list(isCompleted, delete_n_clicks, download_n_clicks, check_value):
# 获取上下文信息
ctx = dash.callback_context if ctx.triggered[0]['prop_id'] == 'delete-btn.n_clicks': for file in check_value:
try:
os.remove(os.path.join('NetDisk', file))
except FileNotFoundError:
pass if ctx.triggered[0]['prop_id'] == 'download-btn.n_clicks': import zipfile with zipfile.ZipFile('NetDisk/打包下载.zip', 'w') as zipobj:
for file in check_value:
try:
zipobj.write(os.path.join('NetDisk', file))
except FileNotFoundError:
pass return [
{'label': file, 'value': file}
for file in os.listdir('NetDisk')
if file != '打包下载.zip'
], '打包下载链接', '/download/打包下载.zip' time.sleep(2) return [
{'label': file, 'value': file}
for file in os.listdir('NetDisk')
if file != '打包下载.zip'
], '', '' if __name__ == '__main__':
app.run_server(debug=True)

  以上就是本文的全部内容,欢迎在评论区与我进行讨论!

(数据科学学习手札114)Python+Dash快速web应用开发——上传下载篇的更多相关文章

  1. (数据科学学习手札102)Python+Dash快速web应用开发——基础概念篇

    本文示例代码与数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的新系列教程Python+Dash快 ...

  2. (数据科学学习手札108)Python+Dash快速web应用开发——静态部件篇(上)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  3. (数据科学学习手札109)Python+Dash快速web应用开发——静态部件篇(中)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  4. (数据科学学习手札112)Python+Dash快速web应用开发——表单控件篇(上)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  5. (数据科学学习手札115)Python+Dash快速web应用开发——交互表格篇(上)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  6. (数据科学学习手札116)Python+Dash快速web应用开发——交互表格篇(中)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  7. (数据科学学习手札117)Python+Dash快速web应用开发——交互表格篇(下)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  8. (数据科学学习手札118)Python+Dash快速web应用开发——特殊部件篇

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  9. (数据科学学习手札119)Python+Dash快速web应用开发——多页面应用

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

随机推荐

  1. C++ Primer笔记

    C++ Primer笔记 ch2 变量和基本类型 声明 extern int i; extern int i = 3.14;//定义 左值引用(绑定零一变量初始值,别名) 不能定义引用的引用:引用必须 ...

  2. css background transparent All In One

    css background transparent All In One opacity ul { max-height: 100px; /* max-height: 187px; */ overf ...

  3. JS Object Deep Copy & 深拷贝 & 浅拷贝

    JS Object Deep Copy & 深拷贝 & 浅拷贝 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Refe ...

  4. Linux bash shell All In One

    Linux bash shell All In One Linux https://tinylab.gitbooks.io/shellbook/content/zh/chapters/01-chapt ...

  5. React Big Changes All in One

    React Big Changes All in One React 重大更新 React Versions React 版本变更 https://reactjs.org/versions/ sema ...

  6. Object to Array

    Object to Array objectToArray(obj = {}, title = `标题`){ let datas = []; if(Object.keys(obj).length) { ...

  7. js 创建简单的表单同步验证器

    SyncValidate declare const uni: any; export interface SyncValidateOpt { [key: string]: SyncValidateF ...

  8. 05_MySQL什么是sql语句

    什么是SQL语言 SQL语言的分类 SQL语言的注意事项 SQL语句的注释 数据的创建.查看及删除 实操: 创建数据表 实操: 数据表的其他操作 查看数据库的数据表 查看表结构: 查看建表语句:

  9. java荷兰国旗问题

    荷兰国旗包含三种颜色:红.白.蓝. 有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列.它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素.等 ...

  10. 【Notes_9】现代图形学入门——光线追踪(基本原理)

    跟着闫令琪老师的课程学习,总结自己学习到的知识点 课程网址GAMES101 B站课程地址GAMES101 课程资料百度网盘[提取码:0000] 目录 光线追踪 为什么要光线追踪 soft shadow ...