Electron下载文件并显示进度条0%——100%

7,808 阅读5分钟

前言

公司有个项目, 分别做了 web端 和 桌面端(Electron实现) , 在web端中下载文件,直接跳转到新页面,使用浏览器自带的下载器即可获得便捷的下载体验. 然而 桌面端 不能直接跳到新页面进行下载而且不能显示下载进度,影响用户体验, 于是领导给到我这样一个需求:

实现一个下载进度条, 可以实时看到下载进度,下载前可以选择文件保存位置

这样做可以弥补了 Web 浏览器的一些先天不足:

  • 支持原生的系统托盘
  • 增强网络状态变更的感知

而且不通过系统浏览器下载携带敏感信息 ,而是在APP里实现理论上更安全

此前虽然接触过 Electron ,但没有深入了解. 这次需求的实现是我在查阅很多资料后实现的, 故写一篇文章来记载整个过程.

一. 页面样式

因为整个项目使用的 Element UI 框架,所以进度条 自然也沿用框架自带的进条度组件,只需要控制dialog弹窗显示与进度百分比就可以了, 界面样式如下: image.png

二. 选择下载位置

在此之前先简单介绍一下本次功能实现所需要的模块, 在主进程和渲染进程进行引入:

主进程模块:

  • ipcMain-> 从主进程到渲染进程的异步通信。
  • dialog -> 显示用于打开和保存文件、警报等的本机系统对话框。
  • shell -> 使用默认应用程序管理文件和 url。

渲染进程模块:

  • ipcRenderer -> 从渲染器进程到主进程的异步通信。

用户在点击下载后首先需要触发系统弹窗, 而不是自定义的进度弹窗. 需要搞清楚先后顺序, 所在在主进程中需要先写好一个 download 事件 .

// 监听渲染进程发出的download事件
ipcMain.on('download', async (evt, args) => {
// 打开系统弹窗 选择文件下载位置
    dialog.showOpenDialog({
      properties: ['openFile', 'openDirectory']
    }, (files) => {
      saveUrl = files[0];  // 保存文件路径
      if (!saveUrl) return; // 如果用户没有选择路径,则不再向下进行
      let url = JSON.parse(args); 
      downloadUrl = url.downloadUrl; // 获取渲染进程传递过来的 下载链接
      mainWindow.webContents.downloadURL(downloadUrl); // 触发 will-download 事件
    })
  });

由于 electron 是基于 chromium 实现的,通过调用 webContents 的 downloadURL 方法,相当于调用了 chromium 底层实现的下载,会忽略响应头信息,触发 will-download 事件。

在渲染进程中,需要在用户点击下载时,发送 download 事件传递要下载的文件链接, 具体代码如下:

 ipcRenderer.send('download', JSON.stringify({
        downloadUrl: href
 }));

image.png

三. 监听下载过程并计算进度

在触发 will-download 事件后, 就进入到了监听下载的过程:

1. 保存下载路径

根据是否存在 item.setSavePath() 语句,来决定是否跳出 save dialog 系统默认保存对话框。这个方法接受一个参数即文件存放路径。 但是, 一定保证路径里包含了文件名! 否则,就算路径不正确它也不会报错而是选择存放在默认路径下。如果不设置这条语句就会跳出默认的系统保存弹窗,无需任何配置, 但是 保存对话框不阻塞进程, 在选择目录的时候,没准都已经下载完了,那么显示下载进度就毫无意义了.

2. 监听下载过程

使用 item的updated事件实时更新触发监听, state 等于progressing表示下载进行中,state 等于interrupted表示下载被打断. 如果状态为 下载进行中 就实时将百分比传到渲染进程进行页面展示:

mainWindow.webContents.send('updateProgressing', value);

3. 计算下载进度

在item的updated事件中 获取的item信息如下:

  • item.getFilename() 下载文件的名称
  • item.getSavePath() 下载文件的路径
  • item.getReceivedBytes() 下载文件已经下载的字节数
  • item.getTotalBytes() 下载文件的总字节数

了解这些后,获取下载进度就容易多了:

parseInt(100 * (item.getReceivedBytes() / item.getTotalBytes()))

4. 监听下载结束

使用 item的 done 事件监听下载完成. state的值:

  • completed -> 下载完成
  • cancelled -> 用户主动取消下载

如果下载完成就使用 electron 的 shell 模块来实现打开文件(openPath)和打开文件所在位置(showItemInFolder)

 shell.showItemInFolder(filePath)

完成代码如下:

主进程:

  mainWindow.webContents.session.on('will-download', (e, item) => {
    const filePath = path.join(saveUrl, item.getFilename());
    item.setSavePath(filePath); // 'C:\Users\kim\Downloads\第12次.zip'
    //监听下载过程,计算并设置进度条进度
    item.on('updated', (evt, state) => {
      if ('progressing' === state) {
        //此处  用接收到的字节数和总字节数求一个比例  就是进度百分比
        if (item.getReceivedBytes() && item.getTotalBytes()) {
          value = parseInt(
            100 * (
              item.getReceivedBytes() / item.getTotalBytes()
            )
          )
        }
        // 把百分比发给渲染进程进行展示
        mainWindow.webContents.send('updateProgressing', value);
        // mac 程序坞、windows 任务栏显示进度
        mainWindow.setProgressBar(value);
      }
    });
    //监听下载结束事件
    item.on('done', (e, state) => {
      //如果窗口还在的话,去掉进度条
      if (!mainWindow.isDestroyed()) {
        mainWindow.setProgressBar(-1);
      }
      //下载被取消或中断了
      if (state === 'interrupted') {
        electron.dialog.showErrorBox('下载失败', `文件 ${item.getFilename()} 因为某些原因被中断下载`);
      }
      // 下载成功后打开文件所在文件夹
      if (state === 'completed') {
        setTimeout(() => {
          shell.showItemInFolder(filePath)
        }, 1000);
      }
    });
  });

在渲染进程中,需要在 生命周期 created 中进行接受:

 ipcRenderer.removeAllListeners('updateProgressing');
    ipcRenderer.on('updateProgressing', (e, value) => {
      this.$nextTick(() => {
        this.downloadStatus = true; // 开启进度弹窗
        this.downloadPercent = value; // 设置下载百分比
      });
    })

总结

Electron 能实现的功能远不止如此,还需要不断探索. 在日常工中可以根据需求结合Electron的原生能力综合实现.这次功能实现踩了不坑, 总结出来一条经验: 在实现过程中,不要对着一条方法使劲琢磨, 其实换一种方法也可以实现想要的需求, 能够让自己感觉豁然开朗.

以上只是我摸索出来的一种实现方法, 如果你有其他更好的方法, 欢迎在评论区畅所欲言!

参考链接:

  1. Electron构建下载文件桌面应用
  2. electron程序,如何监控文件下载进度,并显示进度条?
  3. Electron 官方文档

本文首发于我的博客:Electron下载文件并显示进度条0%——100%