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()
相关推荐
fangzhanpeng1683 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心5 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u01030552711 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u01030552711 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝12 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u01030552713 天前
Java图像处理实战指南
人工智能·1024程序员节
u01030552713 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u01030552715 天前
Java实用类应用精讲
人工智能·1024程序员节
u01030552716 天前
Java I/O核心操作实战
人工智能·1024程序员节
u01030552717 天前
ArrayList操作详解与实战应用
人工智能·1024程序员节