当前位置: 首页 > article >正文

electron展示下载进度条

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

下面我们通过electron提供的will-download监听和element-plus中的ElNotificationElProgress组件实现这一功能
在这里插入图片描述

实现逻辑

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

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

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

// 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中打开的页面

// 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完整文件

<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完整代码

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相关的问题

const { BrowserWindow } = require("electron");
const {initDownload} = require('@/utils/download.js')
var win = null;
async function createWindow() {
	win = new BrowserWindow({
	// 创建相关的参数
	});
	// 为创建的win窗口绑定下载事件
	initDownload(win)
}
createWindow()

http://www.kler.cn/news/365726.html

相关文章:

  • 借助Agent让大模型应用思考、决策并执行任务
  • C++面向对象编程学习
  • 【2024CANN训练营第二季】使用华为云体验AscendC_Sample仓算子运行
  • Django项目实战-图书管理系统之项目搭建
  • Erric Gamma 关于resuable code的采访
  • 使用 v-html 指令渲染的标签, 标签内绑定的 click 事件不生效
  • HarmonyOS(56) 获取自定义组件的唯一ID:getUniqueId()方法
  • 企业如何配合好等级保护测评工作?
  • 聚簇索引与非聚簇索引
  • 【Unity】Unity中调用手机的震动功能 包括安卓和IOS
  • 鸿蒙开发融云demo发送文本消息
  • fpga系列 HDL: 竞争和冒险 01
  • JMeter与大模型融合应用之JMeter创建二级菜单的简单大模型交互
  • 企业自建邮件系统选U-Mail ,功能强大、安全稳定
  • jenkins国内插件源
  • 深入解析 MySQL 数据库:更新和删除
  • 【Java小白图文教程】-05-数组和排序算法详解
  • docker 可用镜像服务地址(2024.10.25亲测可用)
  • 【ChatGPT插件漏洞三连发之二】零点击Github仓库接管
  • Visual 使用技巧合辑
  • 栅格的着色器实现【最完善】
  • 使用AutoDL训练YOLO等计算机视觉网络模型(AutoDL+Xftp+VS Code),附详细操作步骤
  • 打开Windows来体验AIGC或者ChatGPT
  • Python使用asyncio实现异步操作
  • 深度学习系列——RNN/LSTM/GRU,seq2seq/attention机制
  • AI学习指南自然语言处理篇-Transformer模型的编码器-解码器结构