【发布时间】:2021-06-24 06:47:32
【问题描述】:
我正在构建一个 Electron 应用程序,用于管理 NAS 上的照片集合。该 NAS 有两个逻辑卷,名为“alpha”和“beta”。由于我想了解(并修复!)的原因,我的应用程序在尝试针对 beta 上的文件运行 CLI 工具时会收到 ENOENT 错误,但在针对 alpha 版运行 CLI 工具时不会。此外, 允许对两个卷上的文件执行常规 FS 操作(例如 readdir、stat 甚至重命名)而不会出错。例如:应用程序首先了解测试版文件,因为它使用find 扫描文件系统;扫描成功。
我正在使用 CLI 工具 exiftool 从所有这些文件(例如尺寸、捕获设备等)中提取图像元数据。这是我的应用运行的命令,使用 child_process.spawn:
# as a shell command
exiftool -json -n PATH_TO_FILE
// as a node module usable by Electron
const ChildProcess = require('child_process')
module.exports = async function readExif( PATH_TO_FILE ) {
if (typeof PATH_TO_FILE !== 'string') throw new TypeError('PATH_TO_FILE must be a string')
let exifStdout = await new Promise(( resolve, reject ) => {
let stdout = ''
let stderr = ''
const process = ChildProcess.spawn(
'exiftool',
['-json', '-n', PATH_TO_FILE],
{ uid: 501, gid: 20 }
)
process.on('error', ( error ) => reject(error)) // couldn't launch the process
process.stdout.on('data', ( data ) => stdout += data)
process.stderr.on('data', ( data ) => stderr += data)
process.on('close', () => stderr ? reject(stderr) : resolve(stdout))
})
let exifData = JSON.parse(exifStdout)
return exifData[0] // exiftool always returns an array; unwrap it so our return mirrors our input
}
如果我从命令行使用 node 运行它,那么无论目标文件在哪里,它都可以工作。如果 Electron 运行它,它将对 alpha 有效,但对 beta 无效。如果我从 VS Code 内的集成终端运行它,我拒绝了网络文件夹权限,它可以预见地抛出 alpha 和 beta;无论我使用 node 还是 Electron 都是如此,只要它来自 VS Code 内部。
这里是 console.log(error) 作为打包的 Electron 应用程序运行时:
Error: spawn exiftool ENOENT
at Process.ChildProcess._handle.onexit (internal/child_process.js:264:19)
at onErrorNT (internal/child_process.js:456:16)
at processTicksAndRejections (internal/process/task_queues.js:81:21) {
errno: 'ENOENT',
code: 'ENOENT',
syscall: 'spawn exiftool',
path: 'exiftool',
spawnargs: [
'-json',
'-n',
'/Volumes/beta/photographs/IMG_9999.jpg'
]
}
我已经通过实验验证过的事情:
- 如果我从命令行逐字运行命令,它会起作用;所以,我的用户可以读取这些文件,
exiftool也可以 - 将文件从 alpha 移动到 beta(使用 Finder)会导致我的应用程序无法读取它,反之亦然
- 我的实现很好:如果我使用终端中的节点来运行导入
readExif的小型test.js,并手动提供测试版文件的路径,它就可以工作
以上所有让我认为这是一个权限问题,但还有很多其他证据表明权限很好:
- 在对文件使用
exiftool之前,我的应用程序stats 单独使用FS.stat获取文件的大小和时间戳;此统计调用适用于两个卷(注意:应用程序不会直接运行这些调用:它今天stats 文件,然后将文件添加到可能在几天后处理的exiftool扫描队列) - 第一次运行应用程序的每个新版本时,MacOS 要求我重新确认授予网络文件夹权限;我总是同意他们
- 作为实验,我为我的应用程序提供了全盘访问权限(它确实不需要),但这没有任何效果;我专门尝试了这个,因为我的终端应用程序 iTerm2 具有全磁盘访问权限,我认为这可能是导致行为与“生产”不同的原因
- 我的应用 UI 允许我打开单个文件(使用
Electron.shell.openPath(PATH_TO_FILE)),这对于任一卷上的文件都成功
我也尽我所能检查基本文件权限,但我看不出有什么不同。这些卷由用户通过 Finder 安装,Finder 将它们放置在 /Volumes/alpha 和 /Volumes/beta。挂载时,ls 显示这两个节点具有相同的权限:
/Volumes $ ls -lht
total 64
drwx------ 1 Tom staff 16K Mar 25 18:46 alpha
drwx------ 1 Tom staff 16K Mar 25 02:00 beta
lrwxr-xr-x 1 root wheel 1B Dec 31 2019 Macintosh HD -> /
我还手动检查了单张照片的权限,但它们的权限是相同的。我已经使用 NAS 管理工具来验证我的用户帐户是否可以不受限制地访问两个卷;可以,否则我的手动实验都行不通。
而且,作为最后的努力,我将自己的 uid 和 gid 硬编码到 spawn 调用中,以使 Electron 以我自己的身份执行命令,但这也没有任何效果:当 Electron 运行时,我仍然得到 ENOENT exiftool 针对任何处于测试阶段的文件,仅仅是因为该文件处于测试阶段。再次,测试版是我拥有完全访问权限的网络卷,我已多次授予应用程序完全访问权限,该应用程序已经以其他几种方式与之交互,并且在任何方面与 alpha 没有明显不同,我尽管在两台设备上都具有 root 权限,但仍可以检测到。
我什至喊过“是的,有 ENT!”在它。我完全没有想法。
为什么这两种情况不同,我该如何解决?或者失败了,我该如何进一步调试它?
非常欢迎任何建议。
我的包版本:
- 电子:9.2
- 电子封装器:14.2
- 节点:14.13
在运行 MacOS 11.2.3 的 Mac 上运行的电子
NAS 在某种 Linux 上运行 ext4 文件系统。
【问题讨论】:
-
如果你得到一个 ENOENT,这意味着提供的
cwd是无效路径(此处不适用,因为你没有传递cwd)或者该命令不存在(来源:NodeJS docs)。换句话说,该消息与您尝试运行 exiftool 的文件无关。
标签: node.js unix electron file-permissions macos-big-sur