如何将预编译的二进制文件与电子捆绑在一起
How can I bundle a precompiled binary with electron
我正在尝试将预编译的二进制文件包含在电子应用程序中。我从 electron quick start app 开始,修改了我的 renderer.js
文件以包含将文件放在 body 上时触发的代码:
spawn = require('child_process').spawn,
ffmpeg = spawn('node_modules/.bin/ffmpeg', ['-i', clips[0], '-an', '-q:v', '1', '-vcodec', 'libx264', '-y', '-pix_fmt', 'yuv420p', '-vf', 'setsar=1,scale=trunc(iw/2)*2:trunc(ih/2)*2,crop=in_w:in_h-50:0:50', '/tmp/out21321.mp4']);
ffmpeg.stdout.on('data', data => {
console.log(`stdout: ${data}`);
});
ffmpeg.stderr.on('data', data => {
console.log(`stderr: ${data}`);
});
我已将预编译的 ffmpeg 二进制文件放入 node_modules/.bin/
。在开发面板中一切正常,但是当我使用 electron-packager 设置应用程序时,它会在触发时向控制台抛出一个 spawn error ENOENT
。我确实发现 on SO, but the question doesn't seem to be definitively answered. The npm page on electron-packager 确实表明它们可以被捆绑,但我找不到任何关于如何这样做的文档。
如果有人碰巧需要这个问题的答案:我确实有一个解决方案,但我不知道这是否被认为是最佳实践。我找不到包含第 3 方预编译二进制文件的任何好的文档,所以我只是摆弄它直到它最终起作用。这是我所做的(从电子快速入门开始,node.js v6):
我从应用程序目录 运行 使用以下命令将 ffmpeg 二进制文件作为模块包含在内:
mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
ln -s ../ffmpeg/ffmpeg node_modules/.bin/ffmpeg
(将 /usr/local/bin/ffmpeg 替换为您当前的二进制路径,从此处下载)放置 link 允许 electron-packager 包含我保存到 node_modules/ffmpeg/.
然后为了获取捆绑的应用程序路径,我通过 运行 以下命令安装了 npm 包 app-root-dir:
npm i -S app-root-dir
因为我可以获得应用程序路径,所以我只是附加了我的二进制文件的子文件夹并从那里生成。这是我放在 renderer.js:.
中的代码
var appRootDir = require('app-root-dir').get();
var ffmpegpath=appRootDir+'/node_modules/ffmpeg/ffmpeg';
console.log(ffmpegpath);
const
spawn = require( 'child_process' ).spawn,
ffmpeg = spawn( ffmpegpath, ['-i',clips_input[0]]); //add whatever switches you need here
ffmpeg.stdout.on( 'data', data => {
console.log( `stdout: ${data}` );
});
ffmpeg.stderr.on( 'data', data => {
console.log( `stderr: ${data}` );
});
问题是 electron-builder
或 electron-packager
会将您的依赖项捆绑到 asar
文件中。似乎如果依赖项有一个二进制文件 node_modules/.bin
它就足够聪明,不会打包它。
这是关于该主题的 electron-builder
的 documentation for asar packaging。它说
Node modules, that must be unpacked, will be detected automatically
我知道它与 node_modules/.bin
中现有的二进制文件有关。
如果您使用的模块没有自动解压,您可以完全禁用 asar
归档或明确告诉 electron-builder
不要打包某些文件。您在 package.json
文件中这样做:
"build": {
"asarUnpack": [
"**/app/node_modules/some-module/*"
],
针对您的具体情况
我 运行 遇到了与 ffmpeg
相同的问题,这就是我所做的:
- 使用
ffmpeg-static
。这个软件包捆绑了 Windows、Mac 和 Linux 的静态编译的 ffmpeg 二进制文件。它还提供了一种获取 OS 二进制文件完整路径的方法 运行ning: require('ffmpeg-static').path
- 这在开发中可以正常工作,但我们仍然需要解决分发问题。
告诉electron-builder
不要打包ffmpeg-static
模块:
"build": {
"asarUnpack": [
“**/app/node_modules/ffmpeg-static/*”
],
现在我们需要稍微更改代码以使用此代码获得 ffmpeg
的正确路径:require('ffmpeg-static').path.replace('app.asar', 'app.asar.unpacked')
(如果我们正在开发 replace()
不会替换任何好的东西)。
如果您使用的是 webpack(或其他 javascript 捆绑器)
我 运行 解决了 require('ffmpeg-static').path
在渲染器进程中返回相对路径的问题。但问题似乎是 webpack 改变了模块被要求的方式并且阻止 ffmpeg-static
提供完整路径。在 Dev Tools 中,require('ffmpeg-static').path
在手动 运行 时工作正常,但在捆绑代码中执行相同操作时,我总是获得相对路径。所以这就是我所做的。
- 在主进程中,在打开
BrowserWindow
之前添加:global.ffmpegpath = require('ffmpeg-static').path.replace('app.asar', 'app.asar.unpacked')
。主进程中 运行s 的代码没有被 webpack 打包,所以我总是得到这段代码的完整路径。
- 在渲染器进程中以这种方式选择值:
require('electron').remote.getGlobal('ffmpegpath')
我知道我有点晚了,但只是想提一下 ffbinaries
我不久前专门为此目的创建的 npm 包。
它将允许您在应用程序启动期间或在 CI 安装程序中将 ffmpeg/ffplay/ffserver/ffprobe 二进制文件下载到指定位置。可以自动检测平台,也可以手动指定。
我会这样做:
根据 的提示,这是我的代码:
注意:相应地替换或添加 OS 路径。
- 创建目录./resources/mac/bin
- 将您的二进制文件放入此文件夹中
- 创建文件 ./app/binaries.js 并粘贴以下代码:
'use strict';
import path from 'path';
import { remote } from 'electron';
import getPlatform from './get-platform';
const IS_PROD = process.env.NODE_ENV === 'production';
const root = process.cwd();
const { isPackaged, getAppPath } = remote.app;
const binariesPath =
IS_PROD && isPackaged
? path.join(path.dirname(getAppPath()), '..', './Resources', './bin')
: path.join(root, './resources', getPlatform(), './bin');
export const execPath = path.resolve(path.join(binariesPath, './exec-file-name'));
- 创建文件 ./app/get-platform.js 并粘贴以下代码:
'use strict';
import { platform } from 'os';
export default () => {
switch (platform()) {
case 'aix':
case 'freebsd':
case 'linux':
case 'openbsd':
case 'android':
return 'linux';
case 'darwin':
case 'sunos':
return 'mac';
case 'win32':
return 'win';
}
};
- 在 ./package.json 文件中添加以下代码:
"build": {
....
"extraFiles": [
{
"from": "resources/mac/bin",
"to": "Resources/bin",
"filter": [
"**/*"
]
}
],
....
},
- 导入二进制文件路径为:
import { execPath } from './binaries';
#your program code:
var command = spawn(execPath, arg, {});
为什么这样更好?
大多数答案需要一个名为 app-root-dir
的附加包
原始答案不处理 (env=production) build 或 pre-packed 版本适当地。 He/she 只负责开发和 post 打包版本。
我正在尝试将预编译的二进制文件包含在电子应用程序中。我从 electron quick start app 开始,修改了我的 renderer.js
文件以包含将文件放在 body 上时触发的代码:
spawn = require('child_process').spawn,
ffmpeg = spawn('node_modules/.bin/ffmpeg', ['-i', clips[0], '-an', '-q:v', '1', '-vcodec', 'libx264', '-y', '-pix_fmt', 'yuv420p', '-vf', 'setsar=1,scale=trunc(iw/2)*2:trunc(ih/2)*2,crop=in_w:in_h-50:0:50', '/tmp/out21321.mp4']);
ffmpeg.stdout.on('data', data => {
console.log(`stdout: ${data}`);
});
ffmpeg.stderr.on('data', data => {
console.log(`stderr: ${data}`);
});
我已将预编译的 ffmpeg 二进制文件放入 node_modules/.bin/
。在开发面板中一切正常,但是当我使用 electron-packager 设置应用程序时,它会在触发时向控制台抛出一个 spawn error ENOENT
。我确实发现
如果有人碰巧需要这个问题的答案:我确实有一个解决方案,但我不知道这是否被认为是最佳实践。我找不到包含第 3 方预编译二进制文件的任何好的文档,所以我只是摆弄它直到它最终起作用。这是我所做的(从电子快速入门开始,node.js v6):
我从应用程序目录 运行 使用以下命令将 ffmpeg 二进制文件作为模块包含在内:
mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
ln -s ../ffmpeg/ffmpeg node_modules/.bin/ffmpeg
(将 /usr/local/bin/ffmpeg 替换为您当前的二进制路径,从此处下载)放置 link 允许 electron-packager 包含我保存到 node_modules/ffmpeg/.
然后为了获取捆绑的应用程序路径,我通过 运行 以下命令安装了 npm 包 app-root-dir:
npm i -S app-root-dir
因为我可以获得应用程序路径,所以我只是附加了我的二进制文件的子文件夹并从那里生成。这是我放在 renderer.js:.
中的代码var appRootDir = require('app-root-dir').get();
var ffmpegpath=appRootDir+'/node_modules/ffmpeg/ffmpeg';
console.log(ffmpegpath);
const
spawn = require( 'child_process' ).spawn,
ffmpeg = spawn( ffmpegpath, ['-i',clips_input[0]]); //add whatever switches you need here
ffmpeg.stdout.on( 'data', data => {
console.log( `stdout: ${data}` );
});
ffmpeg.stderr.on( 'data', data => {
console.log( `stderr: ${data}` );
});
问题是 electron-builder
或 electron-packager
会将您的依赖项捆绑到 asar
文件中。似乎如果依赖项有一个二进制文件 node_modules/.bin
它就足够聪明,不会打包它。
这是关于该主题的 electron-builder
的 documentation for asar packaging。它说
Node modules, that must be unpacked, will be detected automatically
我知道它与 node_modules/.bin
中现有的二进制文件有关。
如果您使用的模块没有自动解压,您可以完全禁用 asar
归档或明确告诉 electron-builder
不要打包某些文件。您在 package.json
文件中这样做:
"build": {
"asarUnpack": [
"**/app/node_modules/some-module/*"
],
针对您的具体情况
我 运行 遇到了与 ffmpeg
相同的问题,这就是我所做的:
- 使用
ffmpeg-static
。这个软件包捆绑了 Windows、Mac 和 Linux 的静态编译的 ffmpeg 二进制文件。它还提供了一种获取 OS 二进制文件完整路径的方法 运行ning:require('ffmpeg-static').path
- 这在开发中可以正常工作,但我们仍然需要解决分发问题。
告诉
electron-builder
不要打包ffmpeg-static
模块:"build": { "asarUnpack": [ “**/app/node_modules/ffmpeg-static/*” ],
现在我们需要稍微更改代码以使用此代码获得
ffmpeg
的正确路径:require('ffmpeg-static').path.replace('app.asar', 'app.asar.unpacked')
(如果我们正在开发replace()
不会替换任何好的东西)。
如果您使用的是 webpack(或其他 javascript 捆绑器)
我 运行 解决了 require('ffmpeg-static').path
在渲染器进程中返回相对路径的问题。但问题似乎是 webpack 改变了模块被要求的方式并且阻止 ffmpeg-static
提供完整路径。在 Dev Tools 中,require('ffmpeg-static').path
在手动 运行 时工作正常,但在捆绑代码中执行相同操作时,我总是获得相对路径。所以这就是我所做的。
- 在主进程中,在打开
BrowserWindow
之前添加:global.ffmpegpath = require('ffmpeg-static').path.replace('app.asar', 'app.asar.unpacked')
。主进程中 运行s 的代码没有被 webpack 打包,所以我总是得到这段代码的完整路径。 - 在渲染器进程中以这种方式选择值:
require('electron').remote.getGlobal('ffmpegpath')
我知道我有点晚了,但只是想提一下 ffbinaries
我不久前专门为此目的创建的 npm 包。
它将允许您在应用程序启动期间或在 CI 安装程序中将 ffmpeg/ffplay/ffserver/ffprobe 二进制文件下载到指定位置。可以自动检测平台,也可以手动指定。
我会这样做:
根据
注意:相应地替换或添加 OS 路径。
- 创建目录./resources/mac/bin
- 将您的二进制文件放入此文件夹中
- 创建文件 ./app/binaries.js 并粘贴以下代码:
'use strict'; import path from 'path'; import { remote } from 'electron'; import getPlatform from './get-platform'; const IS_PROD = process.env.NODE_ENV === 'production'; const root = process.cwd(); const { isPackaged, getAppPath } = remote.app; const binariesPath = IS_PROD && isPackaged ? path.join(path.dirname(getAppPath()), '..', './Resources', './bin') : path.join(root, './resources', getPlatform(), './bin'); export const execPath = path.resolve(path.join(binariesPath, './exec-file-name'));
- 创建文件 ./app/get-platform.js 并粘贴以下代码:
'use strict'; import { platform } from 'os'; export default () => { switch (platform()) { case 'aix': case 'freebsd': case 'linux': case 'openbsd': case 'android': return 'linux'; case 'darwin': case 'sunos': return 'mac'; case 'win32': return 'win'; } };
- 在 ./package.json 文件中添加以下代码:
"build": { .... "extraFiles": [ { "from": "resources/mac/bin", "to": "Resources/bin", "filter": [ "**/*" ] } ], .... },
- 导入二进制文件路径为:
import { execPath } from './binaries'; #your program code: var command = spawn(execPath, arg, {});
为什么这样更好?
大多数答案需要一个名为 app-root-dir
的附加包
原始答案不处理 (env=production) build 或 pre-packed 版本适当地。 He/she 只负责开发和 post 打包版本。