仅在 readFile() Node js Electron 之后杀死子进程

问题描述

我在 Electron-React 应用程序(在 Windows Pro 10 上)中使用导入的 ffmpeg 二进制文件 ffmpeg-static-electron,并且想要删除保存的裁剪视频,然后从我的 Main process 中终止子进程{1}}。

总体目标是在捕获后裁剪整个屏幕的视频,然后将裁剪后的视频发送到渲染器。

const fsPromises = require('fs').promises
const ffmpeg = require('ffmpeg-static-electron')
const { execFile } = require("child_process")


ipcMain.on("windoze_capture_screen:video_buffer",async (event,buffer) => {
    const temp_directory =  await fsPromises.mkdtemp(await fsPromises.realpath(os.tmpdir()) + path.sep)
    const capture_screen_video_path = path.join(temp_directory,"screen_capture_video.mp4")

    child_object = execFile(`${ffmpeg.path}`,['-i',`${capture_screen_video_path}`,'-vf',`crop=${width}:${height}:${x_pos}:${y_pos}`,`${path.join(temp_directory,'cropped_video.mp4')}`])
            
    child_object.on("exit",async () => {

        // child_object.kill()
        console.log("?Killed -1",child_object.killed)
        
        try { 
            databasePayload.video_buffer = await fsPromises.readFile(path.join(temp_directory,"cropped_video.mp4"),{encoding: 'base64'})
            mainWindow.webContents.send("main_process:video_buffer",databasePayload.video_buffer)
        } catch (error) {
            console.log(error)
        } finally {

            // child_object.kill()
            console.log("?Killed - 2",child_object.killed)
            
            // noASAR required to be set to 'true' in order to remove temp directory in build
            process.noAsar = true 
            fs.rmdir(temp_directory,{recursive: true},(error) => {if (error) {log(error)}})
            process.noASAR = false
            }

            // 3rd scenario
            // child_object.kill()
            console.log("?Killed -3",child_object.killed)   
        })

        // 4th scenario
        console.log("?Killed-4",child_object.killed)   
    })

在运行每个场景时,我得到以下输出

场景 1、2 和 3 - 成功将裁剪的视频发送到渲染器,但不会终止进程。

输出

?Killed-4 false
?Killed -1 false
[ffmpeg version 3.0.1 copyright (c) 2000-2016 the FFmpeg developers built with gcc 5.3.0 (GCC).... etc. ]
?Killed-2 false
?Killed -3 false

场景 4 - 不裁剪视频

?Killed-4 true
?Killed -1 true
Command Failed: C:\Users\XXX\Desktop\windows-electron-forge\node_modules\ffmpeg-static-electron\bin\win\x64\ffmpeg.exe -i C:\Users\XXX\AppData\Local\Temp\4WgnUw\screen_capture_video.mp4 -vf crop=796:763:462:509 C:\Users\XXX\AppData\Local\Temp\4WgnUw\cropped_video.mp4

[Error: ENOENT: no such file or directory,open 'C:\Users\XXX\AppData\Local\Temp\4WgnUw\cropped_video.mp4'] {
errno: -4058,code: 'ENOENT',syscall: 'open',path: 'C:\\Users\\XXX\\AppData\\Local\\Temp\\4WgnUw\\cropped_video.mp4'
}
?Killed - 2 true
?Killed -3 true

出于绝望,我们尝试了最后一个场景。

问题:我哪里出错了?我怀疑这与子进程和父进程之间的 ipc 有关,但不确定将代码放在哪里。

任何帮助将不胜感激!!!

解决方法

一旦您在 exit 函数中收到 .on ("exit",() => { ... }); 事件,就无需终止子进程——它已经自行退出,这意味着它已完成运行并将“死”,因为没有什么可做的了。仅当进程被迫中止当前正在执行的操作时,或者如果它们行为不端并坚持而没有实际执行任何操作时,才需要终止进程(可以在操作系统的任务管理器中观察到)。

正如 Node.js documentation on exit 所述,此事件只会在进程终止时触发。因此,您不再可以在处理程序函数中杀死它,实际上也不需要,因为它不会像所谓的“僵尸”(见上文)那样继续存在,因为它已经完成了执行。

因此,以下功能应该足够了:

child_object.on("exit",async () => {
    try {
        databasePayload.video_buffer = await fsPromises.readFile(path.join(temp_directory,"cropped_video.mp4"),{encoding: 'base64'})
        mainWindow.webContents.send("main_process:video_buffer",databasePayload.video_buffer)
    } catch (error) {
        console.log(error)
    }
    
    // ... your file removal code ...
});

我还想说明三件事:

  • 如果在继续执行后面的代码之前要求实际解析 promise,最好不要使用 await,而是使用函数的同步版本(如果可用)。您的 fs 函数调用将变为:
const fs = require ("fs");
const temp_directory = fs.mkdtempSync (fs.realpathSync (os.tmpdir()) + path.sep)
// ...
databasePayload.video_buffer = fs.readFileSync (path.join (temp_directory,{ encoding: 'base64' })
// ...
try { fs.rmdirSync (temp_directory,{ recursive: true }) } catch (e) { console.log (e); }
  • process.noAsar = true 应该不需要,除非您的临时目录实际上是在应用程序的 ASAR 存档中创建的,除非 Electron Forge 做了一些我不知道的魔术,否则不太可能。考虑调查为什么设置此标志可以解决您的问题(可能是通过转储临时目录的路径)。
  • 单个文件不需要临时目录。除非你打算用这个目录作为更多文件的存储空间,可以考虑改写这个函数来创建一个临时文件,然后删除这个单个文件(这在磁盘硬件上也会更容易,因为只需要执行一次删除而不是两次,但这实际上可以忽略不计,除非您这样做数百次)。