electron 实现文件下载管理器
文件下载是我们开发中比较常见的业务需求,比如:导出 excel。
web 应用文件下载存在一些局限性,通常是让后端将响应的头信息改成 Content-Disposition: attachment; filename=xxx.pdf,触发浏览器的下载行为。
在 electron 中的下载行为,都会触发 session 的 will-download 事件。在该事件里面可以获取到 downloadItem 对象,通过 downloadItem 对象实现一个简单的文件下载管理器:

1. 如何触发下载
由于 electron 是基于 chromium 实现的,通过调用 webContents 的 downloadURL 方法,相当于调用了 chromium 底层实现的下载,会忽略响应头信息,触发 will-download 事件。
// 触发下载
win.webContents.downloadURL(url)
// 监听 will-download
session.defaultSession.on('will-download', (event, item, webContents) => {})
2. 下载流程

3. 功能设计
实现一个简单的文件下载管理器包含以下功能:
- 设置保存路径
- 暂停/恢复和取消
- 下载进度
- 下载速度
- 下载完成
- 打开文件和打开文件所在位置
- 文件图标
- 下载记录
3.1 设置保存路径
如果没有设置保存路径,electron 会自动弹出系统的保存对话框。不想使用系统的保存对话框,可以使用 setSavePath 方法,当有重名文件时,会直接覆盖下载。
item.setSavePath(path)
为了更好的用户体验,可以让用户自己选择保存位置操作。当点击位置输入框时,渲染进程通过 ipc 与主进程通信,打开系统文件选择对话框。

主进程实现代码:
/**
* 打开文件选择框
* @param oldPath - 上一次打开的路径
*/
const openFileDialog = async (oldPath: string = app.getPath('downloads')) => {
if (!win) return oldPath
const { canceled, filePaths } = await dialog.showOpenDialog(win, {
title: '选择保存位置',
properties: ['openDirectory', 'createDirectory'],
defaultPath: oldPath,
})
return !canceled ? filePaths[0] : oldPath
}
ipcMain.handle('openFileDialog', (event, oldPath?: string) => openFileDialog(oldPath))
渲染进程代码:
const path = await ipcRenderer.invoke('openFileDialog', 'PATH')
3.2 暂停/恢复和取消
拿到 downloadItem 后,暂停、恢复和取消分别调用 pause、resume 和 cancel 方法。当我们要删除列表中正在下载的项,需要先调用 cancel 方法取消下载。
3.3 下载进度
在 DownloadItem 中监听 updated 事件,可以实时获取到已下载的字节数据,来计算下载进度和每秒下载的速度。
// 计算下载进度
const progress = item.getReceivedBytes() / item.getTotalBytes()

在下载的时候,想在 Mac 系统的程序坞和 Windows 系统的任务栏展示下载信息,比如:
- 下载数:通过 app 的 badgeCount 属性设置,当为 0 时,不会显示。也可以通过 dock 的 setBadge 方法设置,该方法支持的是字符串,如果不要显示,需要设置为 ''。
- 下载进度:通过窗口的 setProgressBar 方法设置。
由于 Mac 和 Windows 系统差异,下载数仅在 Mac 系统中生效。加上 process.platform === 'darwin' 条件,避免在非 Mac、Linux 系统下出现异常错误。
下载进度(Windows 系统任务栏、Mac 系统程序坞)显示效果:


// mac 程序坞显示下载数:
// 方式一
app.badgeCount = 1
// 方式二
app.dock.setBadge('1')
// mac 程序坞、windows 任务栏显示进度
win.setProgressBar(progress)
3.4 下载速度
由于 downloadItem 没有直接为我们提供方法或属性获取下载速度,需要自己实现。
思路:在 updated 事件里通过 getReceivedBytes 方法拿到本次下载的字节数据减去上一次下载的字节数据。
// 记录上一次下载的字节数据
let prevReceivedBytes = 0
item.on('updated', (e, state) => {
const receivedBytes = item.getReceivedBytes()
// 计算每秒下载的速度
downloadItem.speed = receivedBytes - prevReceivedBytes
prevReceivedBytes = receivedBytes
})
需要注意的是,updated 事件执行的时间约 500ms 一次。

3.5 下载完成
当一个文件下载完成、中断或者被取消,需要通知渲染进程修改状态,通过监听 downloadItem 的 done 事件。
item.on('done', (e, state) => {
downloadItem.state = state
downloadItem.receivedBytes = item.getReceivedBytes()
downloadItem.lastModifiedTime = item.getLastModifiedTime()
// 通知渲染进程,更新下载状态
webContents.send('downloadItemDone', downloadItem)
})
3.6 打开文件和打开文件所在位置
使用 electron 的 shell 模块来实现打开文件(openPath)和打开文件所在位置(showItemInFolder)。
由于 openPath 方法支持返回值
Promise<string>,当不支持打开的文件,系统会有相应的提示,而 showItemInFolder 方法返回值是void。如果需要更好的用户体验,可使用 nodejs 的 fs 模块,先检查文件是否存在。
import fs from 'fs'
// 打开文件
const openFile = (path: string): boolean => {
if (!fs.existsSync(path)) return false
shell.openPath(path)
return true
}
// 打开文件所在位置
const openFileInFolder = (path: string): boolean => {
if (!fs.existsSync(path)) return false
shell.showItemInFolder(path)
return true
}
3.7 文件图标
很方便的是使用 app 模块的 getFileIcon 方法来获取系统关联的文件图标,返回的是 Promise<NativeImage> 类型,我们可以用 toDataURL 方法转换成 base64,不需要我们去处理不同文件类型显示不同的图标。
const getFileIcon = async (path: string) => {
const iconDefault = './icon_default.png'
if (!path) Promise.resolve(iconDefault)
const icon = await app.getFileIcon(path, {
size: 'normal'
})
return icon.toDataURL()
}
3.8 下载记录
随着下载的历史数据越来越多,使用 electron-store 将下载记录保存在本地。
其他
项目的地址:
https://github.com/tal-tech/electron-playground
如果想看更完整的文档,请参考下面文档
Electron-Playground 官方文档
electron 实现文件下载管理器的更多相关文章
- Electron为文件浏览器创建图标(三)
在前面的文章中,请看之前文章,我们已经完成了使用 electron做文件浏览器这么一个应用,现在我们需要为应用创建图标操作.为应用创建图标以后,我们就可以从计算机中与其他应用区分开来,如果我们自己会做 ...
- MCDownloadManager ios文件下载管理器
我们用AFNetworking小试牛刀,写一个简单的下载器来演示功能. 前言 为什么AFNetworking能够成为顶级框架?我们究竟该如何领悟它的精髓所在?这都是很难的问题.安全,高效,流畅,这3个 ...
- 【Electron Playground 系列】文件下载篇
作者:long.woo 文件下载是我们开发中比较常见的业务需求,比如:导出 excel. web 应用文件下载存在一些局限性,通常是让后端将响应的头信息改成 Content-Disposition: ...
- 配置electron
配置语句: git clone https://github.com/electron/electron-quick-start 文件夹名字 打开该文件(我用的webstorm)
- Electron一学习资源收集和练习demo
1.近日为了做项目查资料学习electron,简直头都要炸了,就官方的electron-quick-start的例子进行了基本的练习之后,不断的查资料终于发现一些有用的demo来看源代码学习,一遍看代 ...
- electron 安装使用
1.安装 node.js 链接:http://pan.baidu.com/s/1o7W7BIy 密码:y6od 一路next 我安装在F:\Program Files\node.js下 2.检查nod ...
- 从零开始用electron整个跨平台桌面应用---基础配置篇
1.安装node.npm node以及npm都需要是最新版本(版本过低有坑) 2.安装淘宝镜像cnpm(建议,下载较快) npm install -g cnpm --registry=https:// ...
- Electron入门Demo之桌面应用计算器笔记(二)
码文不易啊,转载请带上本文链接呀,感谢感谢 https://www.cnblogs.com/echoyya/p/14307996.html 在之前总结了一篇自学笔记,通过之前学习到的方法和知识,完成了 ...
- electron 起步
electron 起步 为什么要学 Electron,因为公司需要调试 electron 的应用. Electron 是 node 和 chromium 的结合体,可以使用 JavaScript,HT ...
随机推荐
- centos7中nfs共享的配置方法
NFS是Network File System的缩写,即网络文件系统.客户端通过挂载的方式将NFS服务器端共享的数据目录挂载到本地目录下. 一.nfs为什么需要RPC? 因为NFS支持的功能很多,不同 ...
- JavaFX FileChooser文件选择器,缓存上一次打开的目录
例1:点击按钮Choose File打开文件选择器,并打开指定的目录.这是通过final void setInitialDirectory(final File value)方法实现的. 1 impo ...
- Sprign-mvc系列之Spring快速入门 什么是sprign-mvc spring-mvc的作用及其基本使用+组件解析+注解解析
Spring-mvc 什么是SpringMvc SpringMvc是一种基于java的实现Mvc设计模式的请求驱动类型的轻量级web框架,属于SpringFrameWork的后续产品,已经融合在Spr ...
- python中random的一些用法
#(1)随机小数 import random print(random.random()) #随机大于0 且小于1 之间的小数 ''' 0.9441832228391154 ''' print(ra ...
- Linux 杀毒软件ClamAV安装部署
环境说明 系统安全需求,批量安装免费杀毒软件: 操作系统统一为CentOS 7 x64,在此选择免费开源杀毒软件ClamAV: 两种安装方式 1.yum 安装: 2.源码包编译安装: 安装参考网址: ...
- RHSA-2017:2907-重要: wpa_supplicant 安全更新
[root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) 修复命令: 使用root账号登陆She ...
- BUUCTF_web_三
下面还是简单web的入门题 [GKCTF2020]cve版签到 这次的比赛最简单的题了吧,提示是CVE-2020-7066,但是网上没有几个关于这个漏洞的相关利用的文章,似乎是get_header() ...
- Informatic 内些坑
1. 工作流调用工作流(可实现无规则时间点自由调度) pmcmd startworkflow -sv 集成服务名称 -d 配置域名称 -u Administrator -p Administrato ...
- 多测师讲解python函数 _open_高级讲师肖sir
open()函数 #open() 函数用于打开一个文件,创建一个 file 对象 #Python open() 函数用于打开一个文件,并返回文件对象, # 在对文件进行处理过程都需要使用到这个函数,如 ...
- day20 Pyhton学习 面向对象-类与类之间的关系
一.类与类之间的依赖关系 class Elphant: def __init__(self, name): self.name = name def open(self, ref): print(&q ...