使用Electron app里面的Dropbox Chooser

问题描述 投票:6回答:1

我正在使用Electron(以前称为Atom-Shell)来创建现有Angular Web应用程序的桌面版本。大多数东西开箱即用,但我遇到了Dropbox Chooser的一些问题。

我的网络应用程序允许用户使用选择器从Dropbox导入文件。在Electron中,这会导致为选择器创建新的BrowserWindow。但是新窗口的window.opener属性为null,这基本上使Chooser窗口无法与原始窗口进行通信。这使得它无用,因为有效地选择文件什么都不做。

我知道Slack桌面应用程序使用Electron,并且他们已经能够克服这个问题(Dropbox Chooser在Slack内部工作)。

有谁知道我是否可以/如何在Electron应用程序中使用Dropbox Chooser?

tl; dr我不能在Electron应用程序中使用Dropbox Chooser,因为它打开一个新的BrowserWindow,window.opener设置为null。

javascript dropbox electron
1个回答
0
投票

好的,我设法让这个工作。 window.opener实际上已经设置,其中一个问题是你在Chooser代码中没有正确的来源,这将确保window.opener.postMessage()工作并且消息到达父窗口。还有更多。

1.电子的BrowserWindows

只有当BrowserWindownodeIntegration设置为false时,Dropbox Chooser弹出窗口才能在电子的webSecurity中工作。现在,这些都很棘手,因为如果你从现有的BrowserWindow中打开一个子窗口,你就不能再改变子窗口中的webSecurity了。您可以通过在第三个参数中传递nodeIntegration来更改window.open()调用中的nodeIntegration=no

例如:

window.open('chooser-window.html', '_blank', 'resizable,scrollbars,nodeIntegration=no')

但是我想出了一个更好的解决方案。从main进程打开选择器窗口看起来更有希望,因为我可以控制这两个参数(以及许多其他参数)。此外,我可以轻松地通过window.opener进行基于目标/原点的通信(更多步骤2)。在没有节点集成的情况下创建BrowserWindow使其无法与主进程通信。 require方法和其他节点的好东西都不可用。但是,您可以将预加载脚本传递给此BrowserVindow,其中节点内容可用,您可以重新公开ipcRenderer服务,以便再次建立与主进程的通信。

为了Dropbox Chooser的目的从主进程创建BrowserWindow时,创建如下:

const dropboxProxyWindow = new BrowserWindow({
   webPreferences: {
     nodeIntegration: false,
     webSecurity: false,
     preload: path.join(__dirname, 'dropbox-proxy-preload.js'),
   },
})

并在与dropbox-proxy-preload.js相同的目录中创建一个main.js

// NOTE: This file preloads ipc to hidden dropbox proxy window
//       where nodeIntegration is set to false.

global.ipcRenderer = require('electron').ipcRenderer

这样,我们将有一个BrowserWindow,它可以通过ipc与主进程通信,而不是通过window.opener.postMessage()与父窗口通信的子窗口。这个BrowserWindow将只是我们的电子应用程序的帮助窗口,只有一个代理,将确保实际的Dropbox选择器可以回复我们的应用程序。

2. Dropbox Chooser按钮和Chooser窗口

从Dropbox你会得到一个很好的按钮,你可以粘贴到你的JS / HTML中,一切都可以开箱即用(在浏览器中)。单击按钮后,将打开一个新窗口,您可以选择文件,它们将在JS中进行回调。它看起来像这样:

// in HTML
<script type="text/javascript" src="https://www.dropbox.com/static/api/2/dropins.js" id="dropboxjs" data-app-key="YOUR-APP-KEY"></script>

// in JS via button
var button = Dropbox.createChooseButton(options);
document.getElementById("container").appendChild(button);

// or in JS directly
Dropbox.choose(options);

dropins.js脚本确保与Chooser窗口的通信正常工作。 Dropbox通过使用window.opener.postMessage()脚本自动预填充的第二个参数targetOrigin调用dropins.js来与您的窗口进行通信。此原始网址必须与您在Dropbox开发人员应用管理中定义的内容相匹配。

为了将其移植到电子,我们需要“破解”传递给Chooser窗口的原点,因为电子HTML文件中的window.location不是您可以在Dropbox管理中设置的URL。我们将通过在隐藏的BrowserWindow中打开一个远程HTML文件来打开Chooser。让我们将隐藏的BrowserWindow称为代理窗口。远程HTML将位于我们将添加到Dropbox管理员的域中,并且它将能够与Chooser进行通信。它将使用preload脚本启动,以确保与电子主要过程的通信。从那里我们可以将数据发送到我们的应用程序。加载隐藏代理窗口后,我们会自动点击按钮,以便选择器打开。

3.覆盖dropins.js

还有一个问题。如果我们将隐藏我们的代理窗口,那么从那里打开的所有BrowserWindows也将被隐藏。所以我们需要覆盖这个选项。我们将在dropins.js中,在window.open()调用中,在第三个参数中执行此操作。我们将添加show=1。由于dropins.js默认缩小,我使用Chrome DevTools来美化代码,然后我做了所需的更改。 Here it is in a Gist

最终代码

/main.js

const dropboxProxyWindow = new BrowserWindow({
   webPreferences: {
     nodeIntegration: false,
     webSecurity: false,
     preload: path.join(__dirname, 'dropbox-proxy-preload.js'),
   },
   show: false,
})

const DROPBOX_CHOOSER_LINK = 'https://cdn.yourapp.com/static/dropbox-chooser.html'
dropboxProxyWindow.loadURL(DROPBOX_CHOOSER_LINK)

// NOTE: Catch data from proxy window and send to main.
ipc.on('dropbox-chooser-data', (event, data) => {
  mainWindow.webContents.send('dropbox-chooser-data', data)
})

ipc.on('dropbox-chooser-cancel', () => {
  mainWindow.webContents.send('dropbox-chooser-cancel')
})

/dropbox-proxy-preload.js

global.ipcRenderer = require('electron').ipcRenderer

远程在https://cdn.yourapp.com/static/dropins.jsgist

远程在https://cdn.yourapp.com/static/dropbox-chooser.html

<html>
  <head>
    <title>Dropbox Chooser</title>
    <script type="text/javascript" src="dropins.js" id="dropboxjs" data-app-key="xxx"></script>
  </head>
  <body>
    <div id="container"></div>
    <script type="text/javascript">
      var options = {
        success: function(files) {
          console.debug('Files from dropbox:', files)
          if (!window.ipcRenderer || typeof window.ipcRenderer.send !== 'function') {
            console.warn('Unable to send Dropbox data to App.')
            return
          }
          window.ipcRenderer.send('dropbox-chooser-data', JSON.stringify(files))
        },

        cancel: function() {
          if (!window.ipcRenderer || typeof window.ipcRenderer.send !== 'function') {
            console.warn('Unable to send Dropbox data to App.')
            return
          }
          window.ipcRenderer.send('dropbox-chooser-cancel')
        },

        linkType: "preview",
        multiselect: true,
        folderselect: false,
      };

      var button = Dropbox.createChooseButton(options);
      document.getElementById("container").appendChild(button);
      button.click() // automatically open click on the button so the Chooser opens
    </script>
  </body>
</html>
© www.soinside.com 2019 - 2024. All rights reserved.