Electron开发: 下载管理器

2022-04-18  本文已影响0人  you的日常

如何触发下载行为

由于 Electron 渲染层是基于chromium 的,触发下载的逻辑和 chromium 是一致的,页面中的a标签或者js跳转等等行为都可能触发下载,具体视访问的资源而定。什么样的资源会触发浏览器的下载行为呢?

  1. response header 中的 Content-Disposition 为 attachment。参考 MDN Content-Disposition
  2. response header 中的 Content-Type,是浏览器无法直接打开的文件类型,例如 application/octet-stream,此时取决于浏览器的具体实现了。例子: IE 无法打开 pdf 文件,chrome 可以直接打开 pdf 文件,因此 pdf 类型的 url 在 chrome 上可以直接打开,而在 IE 下会触发下载行为。

在 Electron 中还有一种方法可以触发下载: webContents.download。相当于直接调用 chromium 底层的下载逻辑,忽略 headers 中的那些判断,直接下载。

上述两种下载行为,都会触发session的 will-download 事件,在这里可以获取到关键的 downloadItem 对象

设置文件路径

如果不做任何处理的话,触发下载行为时 Electron 会弹出一个系统 dialog,让用户来选择文件存放的目录。这个体验并不好,因此我们首先需要把这个系统 dialog 去掉。使用 downloadItem.savePath 即可。

// Set the save path, making Electron not to prompt a save dialog.
downloadItem.setSavePath('/tmp/save.pdf');

为文件设置默认下载路径,就需要考虑文件名重复的情况,一般来说会使用文件名自增的逻辑,例如:test.jpg、test.jpg(1)这种格式。文件默认存放目录,也是一个问题,我们统一使用 app.getPath('downloads')作为文件下载目录。为了用户体验,后续提供修改文件下载目录功能即可。

// in main.js 主进程中
const { session } = require('electron');
session.defaultSession.on('will-download', async (event, item) => {
    const fileName = item.getFilename();
    const url = item.getURL();
    const startTime = item.getStartTime();
    const initialState = item.getState();
    const downloadPath = app.getPath('downloads');

    let fileNum = 0;
    let savePath = path.join(downloadPath, fileName);

    // savePath基础信息
    const ext = path.extname(savePath);
    const name = path.basename(savePath, ext);
    const dir = path.dirname(savePath);

    // 文件名自增逻辑
    while (fs.pathExistsSync(savePath)) {
      fileNum += 1;
      savePath = path.format({
        dir,
        ext,
        name: `${name}(${fileNum})`,
      });
    }

    // 设置下载目录,阻止系统dialog的出现
    item.setSavePath(savePath);

     // 通知渲染进程,有一个新的下载任务
    win.webContents.send('new-download-item', {
      savePath,
      url,
      startTime,
      state: initialState,
      paused: item.isPaused(),
      totalBytes: item.getTotalBytes(),
      receivedBytes: item.getReceivedBytes(),
    });

    // 下载任务更新
    item.on('updated', (e, state) => { // eslint-disable-line
      win.webContents.send('download-item-updated', {
        startTime,
        state,
        totalBytes: item.getTotalBytes(),
        receivedBytes: item.getReceivedBytes(),
        paused: item.isPaused(),
      });
    });

    // 下载任务完成
    item.on('done', (e, state) => { // eslint-disable-line
      win.webContents.send('download-item-done', {
        startTime,
        state,
      });
    });
  });

现在触发下载行为,文件就已经会下载到 Downloads 目录了,文件名带有自增逻辑。同时,对下载窗口发送了关键事件,下载窗口可以根据这些事件和数据,创建、更新下载任务

上述步骤在渲染进程使用 remote 实现会有问题,无法获取到实时的下载数据。因此建议在主进程实现。

下载记录

下载功能需要缓存下载历史在本地,下载历史的数据比较多,因此我们使用 nedb 作为本地数据库。

// 初始化 nedb 数据库
const db = nedbStore({ filename, autoload: true });

ipcRenderer.on('new-download-item', (e, item) => {
    // 数据库新增一条新纪录
    db.insert(item);

    // UI中新增一条下载任务
    this.addItem(item);
})

// 更新下载窗口的任务进度
ipcRenderer.on('download-item-updated', (e, item) => {
    this.updateItem(item)
})

// 下载结束,更新数据
ipcRenderer.on('download-item-done', (e, item) => {
    // 更新数据库
    db.update(item);

    // 更新UI中下载任务状态
    this.updateItem(item);
});

此时本地数据库中的数据,是这样的:

{"paused":false,"receivedBytes":0,"savePath":"/Users/ww/Downloads/保利金色佳苑-户型图.jpg","startTime":1560415098.731598,"state":"completed","totalBytes":236568,"url":"https://qhtbdoss.rrr.com/fpimgnew/prod/eee/op/LUBAVDQKN4BE6AABAAAAACY8.jpg?kpm=9V8.32a74ad82d44e7d0.3dba44f.1560415094020","_id":"6AorFZvpI0N8Yzw9"}
{"paused":false,"receivedBytes":0,"savePath":"/Users/ww/Downloads/rrr-12.0.2-stable(1).dmg","startTime":1560415129.488072,"state":"progressing","totalBytes":80762523,"url":"https://qhstaticssl.rrr.com/download/kjl-software12/rrr-12.0.2-stable.dmg?timestamp=1560415129351","_id":"YAeWIy2xoeWTw0Ht"}
{"paused":false,"receivedBytes":0,"savePath":"/Users/ww/Downloads/保利金色佳苑-户型图(1).jpg","startTime":1560418413.240669,"state":"progressing","totalBytes":236568,"url":"https://qhtbdoss.rrr.com/fpimgnew/prod/www/op/LUBBLFYKN4BE6AABAAAAADY8.jpg?kpm=9V8.32a74ad82d44e7d0.3dba44f.1560418409875","_id":"obFLotKillhzTw09"}
{"paused":false,"receivedBytes":0,"savePath":"/Users/ww/Downloads/保利金色佳苑-户型图(1).jpg","startTime":1560418413.240669,"state":"completed","totalBytes":236568,"url":"https://qhtbdoss.rrr.com/fpimgnew/prod/3FO4EGX11S9S/op/LUBBLFYKN4BE6AABAAAAADY8.jpg?kpm=9V8.32a74ad82d44e7d0.3dba44f.1560418409875","_id":"obFLotKillhzTw09"}

在渲染进程初始化的时候,需要读取下载记录,数据按下载时间倒序。读取数量需要做一下限制,否则会影响性能,暂时限制50条。

上一篇下一篇

猜你喜欢

热点阅读