Tob*_*oby 29 imagemagick node.js electron
有关如何将第三方预编译的二进制文件(如imagemagick)包含在电子应用程序中的解决方案吗?有node.js模块,但它们都是包装器或本机绑定到系统范围内安装的库.我想知道是否可以在分发中捆绑预编译的二进制文件.
Ult*_*lly 14
我确实找到了解决方案,但我不知道这是否是最佳实践.我无法找到包括第三方预编译的二进制任何好的文档,所以我只是拨弄着它,直到它最后用我的ffmpeg的二进制工作.这就是我所做的(从电子快速启动开始,node.js v6):
Mac OS X方法
在app目录中,我在Terminal中运行了以下命令,将ffmpeg二进制文件包含为模块:
mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
cd node_modules/.bin
ln -s ../ffmpeg/ffmpeg ffmpeg
Run Code Online (Sandbox Code Playgroud)
(替换/usr/local/bin/ffmpeg为您当前的二进制路径,从此处下载)放置链接允许electron-packager包含我保存到的二进制文件node_modules/ffmpeg/.
然后获取捆绑的应用程序路径(这样我可以使用我的二进制文件的绝对路径......无论我做什么,相对路径似乎都不起作用)我通过运行以下命令安装了npm包app-root-dir命令:
npm i -S app-root-dir
Run Code Online (Sandbox Code Playgroud)
现在我有了根应用程序目录,我只是为我的二进制文件追加子文件夹并从那里生成.这是我放在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}` );
});
Run Code Online (Sandbox Code Playgroud)
Windows方法
要获得捆绑的应用程序路径(以便我可以使用绝对路径来实现我的二进制...相对路径似乎无论我做什么都不起作用)我通过运行以下命令安装了npm包app-root-dir从我的app目录中的命令提示符:
npm i -S app-root-dir
Run Code Online (Sandbox Code Playgroud)在node_modules文件夹中,导航到.bin子文件夹.您需要在此处创建几个文本文件,以告知节点包含刚刚复制的二进制exe文件.使用您喜欢的文本编辑器创建两个文件,其中一个文件名为ffmpeg以下内容:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../ffmpeg/ffmpeg" "$@"
ret=$?
else
node "$basedir/../ffmpeg/ffmpeg" "$@"
ret=$?
fi
exit $ret
Run Code Online (Sandbox Code Playgroud)
第二个文本文件名为ffmpeg.cmd:
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\ffmpeg\ffmpeg" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\ffmpeg\ffmpeg" %*
)
Run Code Online (Sandbox Code Playgroud)接下来,您可以在Windows电子分发中(在renderer.js中)运行ffmpeg,如下所示(我也使用app-root-dir节点模块).请注意添加到二进制路径的引号,如果您的应用程序安装到带有空格的目录(例如C:\Program Files\YourApp),如果没有这些,它将无法工作.
var appRootDir = require('app-root-dir').get();
var ffmpegpath = appRootDir + '\\node_modules\\ffmpeg\\ffmpeg';
const
spawn = require( 'child_process' ).spawn;
var ffmpeg = spawn( 'cmd.exe', ['/c', '"'+ffmpegpath+ '"', '-i', clips_input[0]]); //add whatever switches you need here, test on command line first
ffmpeg.stdout.on( 'data', data => {
console.log( `stdout: ${data}` );
});
ffmpeg.stderr.on( 'data', data => {
console.log( `stderr: ${data}` );
});
Run Code Online (Sandbox Code Playgroud)
tsu*_*iga 13
这是另一种方法,到目前为止已经使用Mac和Windows进行了测试.需要'app-root-dir'包,不需要手动向node_modules dir添加任何内容.
将您的文件放在resources/$ os /下,其中$ os是"mac","linux"或"win".构建过程将根据构建目标OS从这些目录中复制文件.
将extraFiles选择在您的构建CONFIGS如下:
的package.json
"build": {
"extraFiles": [
{
"from": "resources/${os}",
"to": "Resources/bin",
"filter": ["**/*"]
}
],
Run Code Online (Sandbox Code Playgroud)
得到-platform.js
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';
}
};
Run Code Online (Sandbox Code Playgroud)
import { join as joinPath, dirname } from 'path';
import { exec } from 'child_process';
import appRootDir from 'app-root-dir';
import env from './env';
import getPlatform from './get-platform';
const execPath = (env.name === 'production') ?
joinPath(dirname(appRootDir.get()), 'bin'):
joinPath(appRootDir.get(), 'resources', getPlatform());
const cmd = `${joinPath(execPath, 'my-executable')}`;
exec(cmd, (err, stdout, stderr) => {
// do things
});
Run Code Online (Sandbox Code Playgroud)
我认为我使用电子生成器作为基础,env文件生成随之而来.基本上它只是一个JSON配置文件.
上面的答案帮助我弄清楚了它是如何完成的。但是,有一种非常有效的方式来分发二进制文件。
从tsuriga的答案中获取线索,这是我的代码:
注意:相应地替换或添加操作系统路径。
Run Code Online (Sandbox Code Playgroud)'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'));
Run Code Online (Sandbox Code Playgroud)'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'; } };
Run Code Online (Sandbox Code Playgroud)"build": { .... "extraFiles": [ { "from": "resources/mac/bin", "to": "Resources/bin", "filter": [ "**/*" ] } ], .... },
Run Code Online (Sandbox Code Playgroud)import { execPath } from './binaries'; #your program code: var command = spawn(execPath, arg, {});
为什么这样更好?
上面的答案需要一个名为app-root-dir的附加程序包
tsuriga的答案无法正确处理(env = production)构建或预打包的版本。他/她只负责开发和后期打包版本。
TL;博士:
是的你可以!但是它要求你编写自己独立的插件,它不对系统库做任何假设.此外,在某些情况下,您必须确保为所需的操作系统编译插件.
让我们分几个部分来解决这个问题:
- 插件(原生模块):
插件是动态链接的共享对象.
换句话说,您可以编写自己的插件,而不依赖于系统范围的库(例如,通过静态链接所需的模块),其中包含您需要的所有代码.
您必须考虑这种方法是特定于操作系统的,这意味着您需要为每个要支持的操作系统编译插件!(取决于您可能使用的其他库)
- 电子原生模块:
Electron支持本机节点模块,但由于Electron使用的是官方节点不同的V8版本,因此在构建本机模块时必须手动指定Electron标头的位置
这意味着必须重建一个针对节点头构建的本机模块,以便在电子内部使用.你可以在电子文档中找到它.
- 使用电子应用捆绑模块:
我想您希望将您的应用程序作为独立的可执行文件,而无需用户在其计算机上安装电子邮件.如果是这样,我可以建议使用电子封装器.
根据所提供的答案,
这是一个更简洁的解决方案,不需要任何外部依赖项,
/resources/$os)中创建一个文件夹,或者将$os所需的二进制文件复制到那里。假设我们有一个二进制文件并希望将其与电子打包。maclinuxwinffmpegpackage.json并将extraFiles选项放入构建配置中,如下所示: "build": {
"extraFiles": [
{
"from": "resources/${os}", // $os => "mac/linux/win"
"to": "Resources/bin", // for Linux => "resources/bin"
"filter": ["**/*"]
}
],
}
Run Code Online (Sandbox Code Playgroud)
utils.ts使用一些辅助函数创建一个文件(例如:)// utils.ts
import path from 'path';
import { platform } from 'os';
import { app } from 'electron';
export function getPlatform() {
switch (platform()) {
case 'aix':
case 'freebsd':
case 'linux':
case 'openbsd':
case 'android':
return 'linux';
case 'darwin':
case 'sunos':
return 'mac';
case 'win32':
return 'win';
default:
return null;
}
}
export function getBinariesPath() {
const IS_PROD = process.env.NODE_ENV === 'production';
const { isPackaged } = app;
const binariesPath =
IS_PROD && isPackaged
? path.join(process.resourcesPath, './bin')
: path.join(app.getAppPath(), 'resources', getPlatform()!);
return binariesPath;
}
// "ffmpeg" is the binary that we want to package
export const ffmpegPath = path.resolve(path.join(getBinariesPath(), './ffmpeg'));
Run Code Online (Sandbox Code Playgroud)
main.ts)// main.ts
import { exec, spawn } from 'child_process';
import { ffmpegPath } from './utils';
// option 1: using exec
exec(`"${ffmpegPath}" -version`, (err, stdout, stderr) => {
console.log('err, stdout, stderr :>> ', err, stdout, stderr);
});
// option 2: using spawn
const ffmpeg = spawn(ffmpegPath, ['-version']);
ffmpeg.stdout.on('data', (data) => {
console.log(`spawn stdout: ${data}`);
});
ffmpeg.stderr.on('data', (data) => {
console.error(`spawn stderr: ${data}`);
});
ffmpeg.on('close', (code) => {
console.log(`spawn child process exited with code ${code}`);
});
Run Code Online (Sandbox Code Playgroud)