electron v10.1.1 给出 Uncaught TypeError: 无法读取未定义的属性“dialog”,但相同的代码在 electron v9.3.0 中可以工作
我试图在 electron 应用程序中上传文件,该应用程序在 electron v9.3.0 中运行良好,但当我使用 electron v10.1.1 时,它会出现以下错误
Uncaught TypeError: Cannot read property 'dialog' of undefined
在此行
const dialog = electron.remote.dialog;
参见下面的屏幕截图。
main.js内容如下
const { app, BrowserWindow } = require('electron')
function createWindow () {
// Create the browser window.
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// Load the index.html of the app.
win.loadFile('src/index.html')
// Open the DevTools.
win.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
// This method is equivalent to 'app.on('ready', function())'
app.whenReady().then(createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// To stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// On macOS it's common to re-create a window in the
// app when the dock icon is clicked and there are no
// other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
// In this file, you can include the rest of your
// app's specific main process code. You can also
// put them in separate files and require them here.
index.js内容如下
const electron = require('electron');
const path = require('path');
// Importing dialog module using remote
const dialog = electron.remote.dialog;
var uploadFile = document.getElementById('upload');
// Defining a Global file path Variable to store
// user-selected file
global.filepath = undefined;
uploadFile.addEventListener('click', () => {
// If the platform is 'win32' or 'Linux'
if (process.platform !== 'darwin') {
// Resolves to a Promise<Object>
dialog.showOpenDialog({
title: 'Select the File to be uploaded',
defaultPath: path.join(__dirname, '../assets/'),
buttonLabel: 'Upload',
// Restricting the user to only Text Files.
filters: [
{
name: 'Text Files',
extensions: ['txt', 'docx']
}, ],
// Specifying the File Selector Property
properties: ['openFile']
}).then(file => {
// Stating whether dialog operation was
// cancelled or not.
console.log(file.canceled);
if (!file.canceled) {
// Updating the GLOBAL filepath variable
// to user-selected file.
global.filepath = file.filePaths[0].toString();
console.log(global.filepath);
}
}).catch(err => {
console.log(err)
});
}
else {
// If the platform is 'darwin' (macOS)
dialog.showOpenDialog({
title: 'Select the File to be uploaded',
defaultPath: path.join(__dirname, '../assets/'),
buttonLabel: 'Upload',
filters: [
{
name: 'Text Files',
extensions: ['txt', 'docx']
}, ],
// Specifying the File Selector and Directory
// Selector Property In macOS
properties: ['openFile', 'openDirectory']
}).then(file => {
console.log(file.canceled);
if (!file.canceled) {
global.filepath = file.filePaths[0].toString();
console.log(global.filepath);
}
}).catch(err => {
console.log(err)
});
}
});
index.html内容如下
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<!-- https://electronjs.org/docs/tutorial
/security#csp-meta-tag -->
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline';" />
</head>
<body>
<h1>Hello World!</h1> We are using node
<script>
document.write(process.versions.node)
</script>, Chrome
<script>
document.write(process.versions.chrome)
</script>, and Electron
<script>
document.write(process.versions.electron)
</script>.
<h3>File Upload in Electron</h3>
<button id="upload">Upload File</button>
<!-- Adding Individual Renderer Process JS File -->
<script src="index.js"></script>
</body>
</html>
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
enableRemoteModule: true,
nodeIntegration: true
}
})
为了访问渲染器进程上的
remote
模块。我们需要启用
enableRemoteModule
为
true
,因为从较新版本开始,这是默认的
false
。
正如 @tpikatchu 所述:
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
enableremotemodule: true,
nodeIntegration: true
}
})
但
enableremotemodule: true
必须采用驼峰式命名法:
enableRemoteModule: true
参考: https://www.electronjs.org/docs/api/browser-window
附言:抱歉,我刚刚创建了新答案,但我还无法发表评论。
对于 Electron 11.0 及以上版本
远程模块已被弃用。这意味着更新文件所需的对话框对象无法从渲染器 javascript 文件(例如本文中的
index.js
)访问。对话框对象仍可从主入口点访问。为了解决这个问题,您可以使用
ipcMain
和
ipcRenderer
对象来管理入口点和渲染器 javascript 代码之间的通信。
在
main.js
(或您应用中使用的入口点)中添加以下内容:
const {app, BrowserWindow, dialog, ipcMain } = require('electron')
// *** REST OF YOUR CODE GOES HERE ***
ipcMain.on('file-request', (event) => {
// If the platform is 'win32' or 'Linux'
if (process.platform !== 'darwin') {
// Resolves to a Promise<Object>
dialog.showOpenDialog({
title: 'Select the File to be uploaded',
defaultPath: path.join(__dirname, '../assets/'),
buttonLabel: 'Upload',
// Restricting the user to only Text Files.
filters: [
{
name: 'Text Files',
extensions: ['txt', 'docx']
}, ],
// Specifying the File Selector Property
properties: ['openFile']
}).then(file => {
// Stating whether dialog operation was
// cancelled or not.
console.log(file.canceled);
if (!file.canceled) {
const filepath = file.filePaths[0].toString();
console.log(filepath);
event.reply('file', filepath);
}
}).catch(err => {
console.log(err)
});
}
else {
// If the platform is 'darwin' (macOS)
dialog.showOpenDialog({
title: 'Select the File to be uploaded',
defaultPath: path.join(__dirname, '../assets/'),
buttonLabel: 'Upload',
filters: [
{
name: 'Text Files',
extensions: ['txt', 'docx']
}, ],
// Specifying the File Selector and Directory
// Selector Property In macOS
properties: ['openFile', 'openDirectory']
}).then(file => {
console.log(file.canceled);
if (!file.canceled) {
const filepath = file.filePaths[0].toString();
console.log(filepath);
event.send('file', filepath);
}
}).catch(err => {
console.log(err)
});
}
});
将
index.js
中的代码替换为:
const { ipcRenderer } = require('electron');
var uploadFile = document.getElementById('upload');
//upon clicking upload file, request the file from the main process
uploadFile.addEventListener('click', () => {
ipcRenderer.send('file-request');
});
//upon receiving a file, process accordingly
ipcRenderer.on('file', (event, file) => {
console.log('obtained file from main process: ' + file);
});
注意:
我正在使用异步事件。可以通过使用
ipcRenderer.sendSync
并处理返回值来实现同步 - 请查看
electron 文档
了解更多信息。两者的区别在于
sendSync
是一个同步承诺:它会阻塞窗口,直到
ipcMain
发出返回值。对于这样的过程,这似乎是可取的,因为我们可能希望窗口等待文件上传,直到允许用户继续交互。我没有那样做,因为:
- 如果处理不当,它会阻止整个应用程序,如 在 electron 文档中
-
即使应用程序在
sendSync
上被阻止,按钮点击也会被处理。一旦文件对话框关闭,应用程序将触发已进行的所有点击响应,因此阻止并不那么有用。 -
可以使用简单的布尔值并管理
cancel
选项来管理渲染器 javascript 上对话框是否打开 - 如果有人需要,我们很乐意提供此功能!