Electron应用在macOS上读取用户选择的文件时权限被拒绝?

迷人的米娅 阅读 25

我在Electron主进程用dialog.showOpenDialog让用户选择文件后,用fs.readFileSync读取时,macOS报错EPERM: operation not permitted。Windows正常,但macOS即使文件路径正确且用户明确选择后还是失败,这是为什么?

尝试过:fs.access同步检查权限返回true,但读取时还是报错。用绝对路径和相对路径都试过,沙箱模式关闭了,info.plist里也加了NSOpenPanel_USAGE_DESCRIPTION,但问题依旧。


const { dialog } = require('electron').remote
const path = dialog.showOpenDialog({ properties: ['openFile'] })[0]
try {
  const data = fs.readFileSync(path, 'utf8')
} catch (err) {
  console.error(err.code) // 打印出EPERM
}
我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
诸葛永香
问题出在你用了electron.remote,这玩意在新版Electron里行为有点诡异,尤其是在macOS上权限控制更严格。换成ipcRenderer和主进程通信来调用dialog.showOpenDialog

主进程代码:
const { ipcMain, dialog } = require('electron')
ipcMain.handle('open-file', async () => {
const result = await dialog.showOpenDialog({ properties: ['openFile'] })
return result.filePaths[0]
})


渲染进程代码:
const path = await window.electron.ipcRenderer.invoke('open-file')
try {
const data = fs.readFileSync(path, 'utf8')
} catch (err) {
console.error(err)
}


记得检查你的Electron版本,如果是14以上,remote模块已经被废弃了,改用ipcRenderer是最稳妥的办法。
点赞 2
2026-02-16 09:09
Code°爱敏
这个问题是macOS的隐私权限机制导致的,即使用户通过dialog选择了文件,Electron应用在主进程直接用fs读取时依然可能被系统拦截。关键是:你不能只依赖dialog返回的路径字符串就以为拿到了访问权。

从Electron 10开始,macOS上必须使用对话框返回的filePaths和bookmarks来获得临时授权。你现在的方式是错的,应该这样改:

首先确保main.js里开启沙箱或正确配置上下文隔离,然后在dialog.showOpenDialog里加上安全选项:

const { dialog } = require('electron')

async function openFile() {
const result = await dialog.showOpenDialog({
properties: ['openFile'],
securityScopedBookmarks: true // 关键:启用安全书签
})

if (result.canceled) return

const filePath = result.filePaths[0]
const bookmark = result.securityScopedBookmarks[0] // 拿到授权标记

// 需要先‘激活’这个书签授权
if (bookmark) {
try {
// 在renderer用的话需要通过ipc把bookmark传过去并调用startAccessingSecurityScopedResource
const success = process.mas ? startAccessingSecurityScopedResource(bookmark) : true
if (!success) throw new Error('无法获取文件访问权限')

const data = fs.readFileSync(filePath, 'utf8')
console.log(data)

// 用完记得释放
stopAccessingSecurityScopedResource(bookmark)
} catch (err) {
console.error('读取失败:', err.message)
}
}
}


但注意:securityScopedBookmarks只在打包后生效,开发环境可能为空。所以你还得做降级处理,判断是否有bookmark,没有的话直接读(适用于开发阶段)。

另外检查你的info.plist是否加了这些权限:

- NSDocumentsFolderUsageDescription
- NSSystemAdministrationUsageDescription (如果要访问系统目录)

还有记得转义路径里的特殊字符,虽然dialog一般会处理好。

最坑的一点:如果你是在渲染进程用remote调dialog(已经废弃),那更麻烦,因为授权上下文不对。建议完全用ipc通信,在主进程调dialog和fs操作,避免跨进程权限丢失。

总之核心就是一句话:macOS要求对用户选择的文件进行“二次确认式”访问,靠securityScopedBookmarks + 资源访问生命周期管理才能合规读取。别偷懒直接用路径字符串。
点赞 3
2026-02-10 23:01