electron展示下载进度条

我们使用electron下载文件时,会发现不像浏览器一样会有地方展示下载进度,这导致下载一些大文件时不知道下载进度到哪里了

下面我们通过electron提供的will-download监听和element-plus中的ElNotificationElProgress组件实现这一功能

实现逻辑

  1. 触发下载文件这一操作
  2. 监听下载开始、下载进度、下载结束
  3. 根据监听内容操作vnode展示加载进度

1、触发下载文件这一操作

使用electron中ipcMain模块接受页面中发送来的指令

js 复制代码
// main.js 部分代码 通过这个打开****.vue界面 
var win = new BrowserWindow();
//  download.js 部分代码
const { ipcMain, dialog } = require('electron')
let filePath = '';
    // 监听渲染进程发出的download事件
const webContents = win.webContents;
 ipcMain.on('download', (evt, args) => {
     const fileArr = args.split("/");
     let ext = path.extname(args)

     let filters = [{ name: '全部文件', extensions: ['*'] }]
     if (ext && ext !== '.') {
       filters.unshift({
         name: '',
         extensions: [ext.match(/[a-zA-Z]+$/)[0]]
       })
     }
     dialog.showSaveDialog(win, {
         filters,
         defaultPath:args
     }).then( res => {
         if(res.filePath){
             filePath = res.filePath
             webContents.downloadURL(args) // 注意这里必须是下载文件可访问路径。而不是存储路径
         }
     })
 })
 // vue页面中的逻辑 ***.vue
 import { ipcRenderer } from "electron"
 // 触发
 var url = '下载地址.***'
 ipcRenderer.send('download', url)

执行完上面的代码会弹窗另存为下载,会发现下载没有显示进度的地方

2、监听下载开始、下载进度、下载结束

监听webContents中的will-download事件会返回下载相关的一些信息,这里把下载过程中的一些状态和用到的一些参数发送给webContents中打开的页面

js 复制代码
// download.js 部分代码
 webContents.session.on('will-download', (event, item, webContents) => {
        item.setSavePath(filePath) // 这里是存储路径或者存储文件名称
        var filName = path.basename(filePath)
        win.webContents.send('win-will-download',{type:'start',params:{filName}})
        item.on('updated', (event, state) => {
            if (state === 'interrupted') {
                console.log('Download is interrupted but can be resumed')
            } else if (state === 'progressing') {
                if (item.isPaused()) {
                    console.log('Download is paused')
                } else {
                    win.webContents.send('win-will-download',{type:"progress",params:{
                        totalBytes:item.getTotalBytes(),
                        receivedBytes:item.getReceivedBytes()
                    }})
                }
            }

        })
        item.once('done', (event, state) => {
            if (state === 'completed') {
                win.webContents.send('win-will-download',{type:"completed"})
                console.log('Download successfully')
            } else {
                console.log(`Download failed: ${state}`)
            }
        })
    })
 // ***.vue
 //download是展示下载进度的组件,下面会有相应的代码 这里通过ref创建响应数据 vue2的话可以通过 Vue.observable API进行创建
import download from "@/components/download/index.vue"
import { ElNotification } from 'element-plus'
import { h, ref } from 'vue'
var totalBytes = ref(0)
var receivedBytes = ref(0)

 mounted(){
	 ipcRenderer.removeAllListeners('win-will-download')
	 ipcRenderer.on('win-will-download', this.winDownLoadFun)
 },
 methods:{
 	
 	winDownLoadFun(event, data) {
      if (data.type == 'start') {
        this.notice && this.notice.close && this.notice.close()
        var progress = h(download, {
          totalBytes: totalBytes,
          receivedBytes: receivedBytes,
          filName: data.params.filName,
          onClose: () => {
            totalBytes.value = 0
            receivedBytes.value = 0
            this.notice && this.notice.close && this.notice.close()
          }
        })
        this.notice = ElNotification({
          title: '下载进度',
          position: 'bottom-right',
          duration: 0,
          showClose: false,
          message: progress,
          onClose: () => {
            this.notice = null
          }
        })
      }
      else if (data.type == 'progress') {
        totalBytes.value = data.params.totalBytes
        receivedBytes.value = data.params.receivedBytes
      } else if (data.type == 'completed') {
        receivedBytes.value = totalBytes.value
      }
    },
 } 

3、根据监听内容操作vnode展示加载进度

下面是download/index.vue完整文件

html 复制代码
<template>
    <div style="width: 100%;">
        <div>
            <div @click="$emit('close')" v-if="percentage == 100" style="position: absolute;top: 15px;right: 15px;cursor: pointer;">关闭</div>
            <div class="task-item">
                <img class="img" src="@/assets/image/zip-1.png"></img>
                <div class="name">
                    <div>{{filName}}</div>
                    <div class="progress1">{{limitFormat(receivedBytes.value)}}/{{limitFormat(totalBytes.value)}}</div>
                </div>
            </div>

            <div>
                <el-progress :show-text="false" :percentage="percentage" />
            </div>
        </div>
    </div>
</template>

<script>
import { ElMessage, ElProgress } from 'element-plus'
import { ref } from 'vue'
export default {
    name: 'download',
    props: {
        filName:{
            type:String,
            default:""
        },
        totalBytes: {
            default() {
                return ref(0)
            }
        },
        receivedBytes: {
            default() {
                return ref(0)
            }
        }
    },
    computed:{
        percentage(){
           return parseFloat((((this.receivedBytes.value / this.totalBytes.value) ||0 )* 100).toFixed(2)) 
        }
    },
    watch:{
        percentage(){
            if(this.percentage == 100 && this.totalBytes.value != 0){
                ElMessage({
                    message:"下载完成!",
                    type:"success"
                })
            }
        },
    },
    methods: {
        limitFormat(limit) {
            var size = "";
            if (limit < 0.1 * 1024) { //小于0.1KB,则转化成B
                size = limit.toFixed(2) + "B"
            } else if (limit < 0.1 * 1024 * 1024) { //小于0.1MB,则转化成KB
                size = (limit / 1024).toFixed(2) + "KB"
            } else if (limit < 0.1 * 1024 * 1024 * 1024) { //小于0.1GB,则转化成MB
                size = (limit / (1024 * 1024)).toFixed(2) + "MB"
            } else { //其他转化成GB
                size = (limit / (1024 * 1024 * 1024)).toFixed(2) + "GB"
            }

            var sizeStr = size + ""; //转成字符串
            var index = sizeStr.indexOf("."); //获取小数点处的索引
            var dou = sizeStr.substr(index + 1, 2) //获取小数点后两位的值
            if (dou == "00") { //判断后两位是否为00,如果是则删除00
                return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2)
            }
            return size;
        }
    },

}
</script>

<style scoped>
.task-item {
    width: 280px;
    display: flex;
    align-items: center;
    margin-bottom: 6px;
}

.progress1 {
    font-size: 12px;
    margin-top: -4px;
    color: #999;
}

.task-item i {}

.img {
    width: 32px;
    height: 32px;
    display: block;
    margin-right: 14px;

}
</style>

download.js完整代码

js 复制代码
const { ipcMain, dialog, shell } = require('electron')
const path = require('path')
const fs = require('fs');
const { type } = require('os');
exports.initDownload = function (win) {
    let filePath = '';
    // 监听渲染进程发出的download事件
    const webContents = win.webContents;
    ipcMain.on('download', (evt, args) => {
        const fileArr = args.split("/");
        let ext = path.extname(args)

        let filters = [{ name: '全部文件', extensions: ['*'] }]
        if (ext && ext !== '.') {
          filters.unshift({
            name: '',
            extensions: [ext.match(/[a-zA-Z]+$/)[0]]
          })
        }
        dialog.showSaveDialog(win, {
            filters,
            defaultPath:args
        }).then( res => {
            if(res.filePath){
                filePath = res.filePath
                webContents.downloadURL(args) // 注意这里必须是下载文件可访问路径。而不是存储路径
            }
        })
    })
    webContents.session.on('will-download', (event, item, webContents) => {
        item.setSavePath(filePath) // 这里是存储路径或者存储文件名称
        var filName = path.basename(filePath)
        win.webContents.send('win-will-download',{type:'start',params:{filName}})
        item.on('updated', (event, state) => {
            if (state === 'interrupted') {
                console.log('Download is interrupted but can be resumed')
            } else if (state === 'progressing') {
                if (item.isPaused()) {
                    console.log('Download is paused')
                } else {
                    win.webContents.send('win-will-download',{type:"progress",params:{
                        totalBytes:item.getTotalBytes(),
                        receivedBytes:item.getReceivedBytes()
                    }})
                }
            }

        })
        item.once('done', (event, state) => {
            if (state === 'completed') {
                win.webContents.send('win-will-download',{type:"completed"})
                console.log('Download successfully')
            } else {
                console.log(`Download failed: ${state}`)
            }
        })
    })
}

main.js中使用代码

这里的main.js是electron的主进程文件,不是vue相关的问题

js 复制代码
const { BrowserWindow } = require("electron");
const {initDownload} = require('@/utils/download.js')
var win = null;
async function createWindow() {
	win = new BrowserWindow({
	// 创建相关的参数
	});
	// 为创建的win窗口绑定下载事件
	initDownload(win)
}
createWindow()
相关推荐
希忘auto1 天前
详解Redis的常用命令
redis·1024程序员节
yaosheng_VALVE2 天前
探究全金属硬密封蝶阀的奥秘-耀圣控制
运维·eclipse·自动化·pyqt·1024程序员节
dami_king2 天前
SSH特性|组成|SSH是什么?
运维·ssh·1024程序员节
一个通信老学姐7 天前
专业125+总分400+南京理工大学818考研经验南理工电子信息与通信工程,真题,大纲,参考书。
考研·信息与通信·信号处理·1024程序员节
sheng12345678rui7 天前
mfc140.dll文件缺失的修复方法分享,全面分析mfc140.dll的几种解决方法
游戏·电脑·dll文件·dll修复工具·1024程序员节
huipeng9268 天前
第十章 类和对象(二)
java·开发语言·学习·1024程序员节
earthzhang20218 天前
《深入浅出HTTPS》读书笔记(19):密钥
开发语言·网络协议·算法·https·1024程序员节
爱吃生蚝的于勒9 天前
计算机基础 原码反码补码问题
经验分享·笔记·计算机网络·其他·1024程序员节
earthzhang20219 天前
《深入浅出HTTPS》读书笔记(20):口令和PEB算法
开发语言·网络协议·算法·https·1024程序员节
一个通信老学姐9 天前
专业140+总分410+浙江大学842信号系统与数字电路考研经验浙大电子信息与通信工程,真题,大纲,参考书。
考研·信息与通信·信号处理·1024程序员节