Electron.js:如何为一个窗口创建一个单独的下载 webContents.session?

Posted

技术标签:

【中文标题】Electron.js:如何为一个窗口创建一个单独的下载 webContents.session?【英文标题】:Electron.js: how to create a separate download webContents.session for a window? 【发布时间】:2021-08-25 16:16:11 【问题描述】:

我有一个 Electron file manager app,它为不同的目的创建了 2 个窗口:

quickView 用于预览本地文件的渲染器窗口。它使用“will-download”侦听器通过阻止下载来检测不受支持的文件。

main 主渲染器窗口。它使用“will-download”监听器来下载文件。

每个人都有自己的will-download 听众附加到他们的会话中。但由于某种原因,quickView 监听器覆盖了main 监听器。

窗口 1

在以下行中,我正在为“主”进程创建一个will-download 侦听器。这个监听器的目的是下载文件:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/55fd2462cf83898610883191807b7488fb5bdf89/src/utils/downloadManager.js#L133

win.webContents.session.on('will-download', listener)

下面一行中的windows.main参数是上面一行中的win引用:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/55fd2462cf83898610883191807b7488fb5bdf89/src/electronMain.js#L516

const resultInfo = await downloadManager.download(windows.main, 

窗口 2

在下面的行中,我正在为“quickView”窗口创建一个will-download 侦听器。此侦听器的目的是检测不受支持的文件(在 Chromium 中触发下载事件)并阻止下载事件:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/55fd2462cf83898610883191807b7488fb5bdf89/src/electronMain.js#L232

windows.quickViewWindow.webContents.session.once('will-download', _willDownloadHandler)

我还没有找到另一种方法来检测不受支持的文件,这就是我首先使用will-download 事件的原因。

问题

由于某种原因,quickView 窗口的will-download 处理程序覆盖了main 的处理程序:

当我在此处触发应用更新下载事件时(来自main 进程):

https://github.com/aleksey-hoffman/sigma-file-manager/blob/55fd2462cf83898610883191807b7488fb5bdf89/src/electronMain.js#L516

const resultInfo = await downloadManager.download(windows.main, 

它触发quickView渲染器窗口的事件处理程序:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/55fd2462cf83898610883191807b7488fb5bdf89/src/electronMain.js#L241

function _willDownloadHandler (event, item, webContents) 
  ...
  windows.main.webContents.send('load:webview::failed', path: fileURL)

部分修复

我通过为quickView 窗口的会话指定自定义分区名称部分解决了this commit 中的问题,因此它不使用默认会话并且不会覆盖@987654354 创建的will-download 侦听器@:

主要流程:

windows.quickViewWindow = new electron.BrowserWindow(
  ...
  webPreferences: 
    partition: 'quickPreview',

...

windows.quickViewWindow.webContents.session.once(
  'will-download',
  (event, item, webContents) => 
    event.preventDefault()
    ...
  
)

quickViewWindow.html

ipcRenderer.on('load:webview', (event, data) => 
  ...
  webviewNode.setAttribute('partition', 'quickPreview')

但是这个修复导致了另一个问题:

快速查看窗口在生产版本中停止工作(可能与协议不使用非默认会话分区有关)

将自定义分区设置为 webview 会导致在创建包含此 webview 的窗口时在生产中弹出 Windows 协议链接关联:

我认为这可能是由electron-builder-plugin 创建的自定义app:// 协议引起的。似乎弹出窗口是由“应用程序”链接触发的。

或者可能是因为我在这条线附近的某处创建窗口时设置了错误的协议:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/47ce65bdac78e5c9b17315f16623d40d81dcf1bb/src/electronMain.js#L203

重现:

    下载项目
git clone https://github.com/aleksey-hoffman/sigma-file-manager.git
cd sigma-file-manager
npm install
git checkout 47ce65b
npm run electron:build
    ./dist_electron 安装构建的应用程序 在应用启动期间,您可以看到弹出窗口

注意事项:

我刚刚回滚了 47ce65b 提交并添加了一些测试值 所以更容易调试

切换到最新提交并创建生产版本:

git checkout 5246252
npm run electron:build

electronMain.js 内的所有console.log() 都显示在终端(命令行)窗口(不是开发者工具控制台)中。

触发快速查看功能:

在导航器页面上选择任何支持的文件(图像/文本/等) Press Space(快速查看窗口应该打开)

要触发下载事件,您只需打开“导航器”页面并从 Internet 拖放任何文件(或网站 URL)即可。它将触发错误的 will-download 事件处理程序(quickView 窗口的处理程序),您应该会看到控制台消息。

包含此 web 视图的 quickView 窗口是在 app.ready 事件上创建的。指定分区后,在快速查看窗口创建后会立即弹出:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/47ce65bdac78e5c9b17315f16623d40d81dcf1bb/src/electronMain.js#L698

更新:

较小的复制示例:

我能够用这段代码重现它:

let window1 = null
let window2 = null

electron.app.on('ready', async () => 
  createWindow1()
  createWindow2()

  setTimeout(() => 
    console.log('trigger window 1 download')
    window1.webContents.downloadURL('https://***.com')
  , 1000)
)


function createWindow2 () 
  window1.webContents.session.once('will-download', downloadHandler1)
  window2.webContents.session.once('will-download', downloadHandler2)


function createWindow1 () 
  window1 = new electron.BrowserWindow()
  window1.loadURL('app://./quickViewWindow.html')
  window1.webContents.session.once('will-download', downloadHandler1)


function createWindow2 () 
  window2 = new electron.BrowserWindow()
  window2.loadURL('app://./quickViewWindow.html')
  window2.webContents.session.once('will-download', downloadHandler2)


function downloadHandler1 (event, item, webContents) 
  console.log('window will-download handler 1')


function downloadHandler2 (event, item, webContents) 
  console.log('window will-download handler 2')


setTimeout 运行时,我看到以下console.log() 消息:

trigger window 1 download
window will-download handler 1
window will-download handler 2

从日志中可以看出,will-download 事件触发了两个窗口的事件处理程序

如果我为每个窗口指定一个单独的分区,共享事件处理程序的问题就会得到解决,但我遇到了上面提到的第二个问题 - 启动时会弹出链接关联

window1 = new electron.BrowserWindow(
  webPreferences: 
    partition: 'partition1',
  
)
window2 = new electron.BrowserWindow(
  webPreferences: 
    partition: 'partition2',
  
)

【问题讨论】:

基本上你想处理来自两个进程的相同事件?为什么不使用ipc.send() 将消息从主进程发送到渲染器进程?这样,您将在主进程中处理下载并将包含所需数据的消息发送到处理程序内的渲染器进程 @TasosBu 好吧,我将单独的 will-download 事件附加到 windows.mainwindows.quickViewWindow 但由于某种原因,当 windows.mainwill-download 事件发生时,它运行windows.quickViewWindow 事件侦听器的事件处理程序。我不知道为什么第二个窗口的处理程序会覆盖第一个窗口的事件处理程序。我通过为第二个窗口指定一个单独的分区名称来修复它,这样它们就不会共享相同的 webContents.session,但正如我所提到的,它导致了另一个问题 知道了!如果在您的部分修复中,您使用windows.quickViewWindow.webContents.session.fromPartition('quickPreview').on 而不是windows.quickViewWindow.webContents.session.once 怎么办?我认为您需要指定处理程序将附加到哪个会话 我相信这可能会导致“Windows Store”窗口弹出,因为您将处理程序附加到不存在的会话事件。该事件存在于您的 quickPreview 会话中并且它没有处理程序 @TasosBu 感谢小伙伴的建议,可惜没有解决问题,在生产环境中还是会出现这个弹窗。我认为这与协议有关。该应用默认使用app:// 协议。我尝试指定一个自定义的(出于安全原因,为了打开本地文件,Electron 强制您这样做),所以我在第 196 行的./src/electronMain.js 中添加了这一行:productionPath = `sigma-file-manager://$__static/quickViewWindow.html` 但现在它显示 Windows 弹出说我需要一个应用程序来打开“sigma-file-manager”链接 【参考方案1】:

我想通了。如果这是错误的方法,请告诉我。

这是我修复它的方法:

修复问题 #1:

为窗口设置一个自定义分区名称,因此它使用自己的webContents.session,而不是共享默认分区。

主要流程:

windows.quickViewWindow = new electron.BrowserWindow(
  ...
  webPreferences: 
    partition: 'quickView',

...

windows.quickViewWindow.webContents.session.once(
  'will-download',
  (event, item, webContents) => 
    event.preventDefault()
    ...
  
)

quickViewWindow.html:

ipcRenderer.on('load:webview', (event, data) => 
  ...
  webviewNode.setAttribute('partition', 'quickView')

修复问题 #2:

在窗口URL的生产路径中设置file://协议:

productionPath = `file://$__static/quickViewWindow.html`

这是提交:https://github.com/aleksey-hoffman/sigma-file-manager/commit/31208809cda7614a7c2f32237ae14f6c9c602f8f

【讨论】:

以上是关于Electron.js:如何为一个窗口创建一个单独的下载 webContents.session?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 icarousel 中的每个按钮创建单独的操作?

如何为单个项目中运行的每个批次创建单独的日志文件?

如何为两个单独的 Dbus Python 程序创建 Dbus Mainloop

如何为每种风格生成单独的签名APK?

如何为主窗口创建的小部件创建 QCloseEvent

在 Angular 项目中,如何为项目单独创建路由文件?