Electron应用在macOS上读取用户选择的文件时权限被拒绝?
我在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
}
electron.remote,这玩意在新版Electron里行为有点诡异,尤其是在macOS上权限控制更严格。换成ipcRenderer和主进程通信来调用dialog.showOpenDialog。主进程代码:
渲染进程代码:
记得检查你的Electron版本,如果是14以上,
remote模块已经被废弃了,改用ipcRenderer是最稳妥的办法。从Electron 10开始,macOS上必须使用对话框返回的filePaths和bookmarks来获得临时授权。你现在的方式是错的,应该这样改:
首先确保main.js里开启沙箱或正确配置上下文隔离,然后在dialog.showOpenDialog里加上安全选项:
但注意:securityScopedBookmarks只在打包后生效,开发环境可能为空。所以你还得做降级处理,判断是否有bookmark,没有的话直接读(适用于开发阶段)。
另外检查你的info.plist是否加了这些权限:
- NSDocumentsFolderUsageDescription
- NSSystemAdministrationUsageDescription (如果要访问系统目录)
还有记得转义路径里的特殊字符,虽然dialog一般会处理好。
最坑的一点:如果你是在渲染进程用remote调dialog(已经废弃),那更麻烦,因为授权上下文不对。建议完全用ipc通信,在主进程调dialog和fs操作,避免跨进程权限丢失。
总之核心就是一句话:macOS要求对用户选择的文件进行“二次确认式”访问,靠securityScopedBookmarks + 资源访问生命周期管理才能合规读取。别偷懒直接用路径字符串。