将预编译的二进制文件捆绑到电子应用程序中

时间:2021-07-17 15:53:00

Is there a good solution on how to include third party pre compiled binaries like imagemagick into an electron app? there are node.js modules but they are all wrappers or native binding to the system wide installed libraries. I wonder if it's possible to bundle precompiled binaries within the distribution.

是否有一个很好的解决方案,如何将第三方预编译的二进制文件,如imagemagick包含在电子应用程序中?有node.js模块,但它们都是包装器或本机绑定到系统范围内安装的库。我想知道是否可以在分发中捆绑预编译的二进制文件。

3 个解决方案

#1


9  

I did find a solution to this, but I have no idea if this is considered best practice. I couldn't find any good documentation for including 3rd party precompiled binaries, so I just fiddled with it until it finally worked with my ffmpeg binary. Here's what I did (starting with the electron quick start, node.js v6):

我确实找到了解决方案,但我不知道这是否是最佳实践。我找不到包含第三方预编译二进制文件的任何好文档,所以我只是摆弄它直到最终使用我的ffmpeg二进制文件。这就是我所做的(从电子快速启动开始,node.js v6):

Mac OS X method

Mac OS X方法

From the app directory I ran the following commands in Terminal to include the ffmpeg binary as a module:

在app目录中,我在Terminal中运行以下命令,将ffmpeg二进制文件包含为模块:

mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
cd node_modules/.bin
ln -s ../ffmpeg/ffmpeg ffmpeg

(replace /usr/local/bin/ffmpeg with your current binary path, download it from here) Placing the link allowed electron-packager to include the binary I saved to node_modules/ffmpeg/.

(将/ usr / local / bin / ffmpeg替换为您当前的二进制路径,从此处下载)放置链接允许electron-packager包含我保存到node_modules / ffmpeg /的二进制文件。

Then to get the bundled app path (so that I could use an absolute path for my binary... relative paths didn't seem to work no matter what I did) I installed the npm package app-root-dir by running the following command:

然后获取捆绑的应用程序路径(这样我可以使用我的二进制的绝对路径...无论我做什么,相对路径似乎都不起作用)我通过运行以下命令安装了npm包app-root-dir命令:

npm i -S app-root-dir

Now that I had the root app directory, I just append the subfolder for my binary and spawned from there. This is the code that I placed in renderer.js:.

现在我有了根应用程序目录,我只是为我的二进制文件附加子文件夹并从那里生成。这是我放在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}` );
    });

Windows Method

Windows方法

  1. Open your electron base folder (electron-quick-start is the default name), then go into the node_modules folder. Create a folder there called ffmpeg, and copy your static binary into this directory. Note: it must be the static version of your binary, for ffmpeg I grabbed the latest Windows build here.
  2. 打开电子基础文件夹(electron-quick-start是默认名称),然后进入node_modules文件夹。创建一个名为ffmpeg的文件夹,并将静态二进制文件复制到此目录中。注意:它必须是二进制文件的静态版本,对于ffmpeg,我在这里抓住了最新的Windows版本。
  3. To get the bundled app path (so that I could use an absolute path for my binary... relative paths didn't seem to work no matter what I did) I installed the npm package app-root-dir by running the following command from a command prompt in my app directory:

    要获取捆绑的应用程序路径(以便我可以使用绝对路径来实现我的二进制...相对路径似乎无论我做了什么都没有用)我通过运行以下命令安装了npm包app-root-dir从我的app目录中的命令提示符:

     npm i -S app-root-dir
    
  4. Within your node_modules folder, navigate to the .bin subfolder. You need to create a couple of text files here to tell node to include the binary exe file you just copied. Use your favorite text editor and create two files, one named ffmpeg with the following contents:

    在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
    

    And the the second text file, named ffmpeg.cmd:

    第二个文本文件名为ffmpeg.cmd:

    @IF EXIST "%~dp0\node.exe" (
     "%~dp0\node.exe"  "%~dp0\..\ffmpeg\ffmpeg" %*
    ) ELSE (
       @SETLOCAL
     @SET PATHEXT=%PATHEXT:;.JS;=;%
     node  "%~dp0\..\ffmpeg\ffmpeg" %*
    )
    

Next you can run ffmpeg in your Windows electron distribution (in renderer.js) as follows (I'm using the app-root-dir node module as well). Note the quotes added to the binary path, if your app is installed to a directory with spaces (eg C:\Program Files\YourApp) it won't work without these.

接下来,您可以在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}` );
 });

#2


8  

Here's another method, tested with Mac and Windows so far. Requires 'app-root-dir' package, doesn't require adding anything manually to node_modules dir.

这是另一种方法,到目前为止用Mac和Windows测试过。需要'app-root-dir'包,不需要手动向node_modules dir添加任何内容。

  1. Put your files under resources/$os/, where $os is either "mac", "linux", or "win". The build process will copy files from those directories as per build target OS.

    把你的文件放在resources / $ os /下,其中$ os是“mac”,“linux”或“win”。构建过程将根据构建目标OS从这些目录中复制文件。

  2. Put extraFiles option in your build configs as follows:

    在构建配置中添加extraFiles选项,如下所示:

package.json

的package.json

  "build": {
    "extraFiles": [
      {
        "from": "resources/${os}",
        "to": "Resources/bin",
        "filter": ["**/*"]
      }
    ],
  1. Use something like this to determine the current platform.
  2. 使用这样的东西来确定当前的平台。

get-platform.js

得到-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';
  }
};
  1. Call the executable from your app depending on env and OS. Here I am assuming built versions are in production mode and source versions in other modes, but you can create your own calling logic.
  2. 根据env和OS调用应用程序中的可执行文件。这里我假设构建版本处于生产模式,源代码版本处于其他模式,但您可以创建自己的调用逻辑。
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
});

I think I was using electron-builder as base, the env file generation comes with it. Basically it's just a JSON config file.

我认为我使用电子生成器作为基础,env文件生成随之而来。基本上它只是一个JSON配置文件。

#3


6  

tl;dr:

TL;博士:

yes you can! but it requires you to write your own self-contained addon which does not make any assumptions on system libraries. Moreover in some cases you have to make sure that your addon is compiled for the desired OS.

是的你可以!但是它要求你编写自己独立的插件,它不对系统库做任何假设。此外,在某些情况下,您必须确保为所需的操作系统编译插件。


Lets break this question in several parts:

让我们分几个部分来解决这个问题:

- Addons (Native modules):

- 插件(原生模块):

Addons are dynamically linked shared objects.

插件是动态链接的共享对象。

In other words you can just write your own addon with no dependency on system wide libraries (e.g. by statically linking required modules) containing all the code you need.

换句话说,您可以编写自己的插件,而不依赖于系统范围的库(例如,通过静态链接所需的模块),其中包含您需要的所有代码。

You have to consider that such approach is OS-specific, meaning that you need to compile your addon for each OS that you want to support! (depending on what other libraries you may use)

您必须考虑这种方法是特定于操作系统的,这意味着您需要为每个要支持的操作系统编译插件! (取决于您可能使用的其他库)

- Native modules for electron:

- 电子原生模块:

The native Node modules are supported by Electron, but since Electron is using a different V8 version from official Node, you have to manually specify the location of Electron's headers when building native modules

Electron支持本机节点模块,但由于Electron使用的是官方节点不同的V8版本,因此在构建本机模块时必须手动指定Electron标头的位置

This means that a native module which has been built against node headers must be rebuilt to be used inside electron. You can find how in electron docs.

这意味着必须重建一个针对节点头构建的本机模块,以便在电子内部使用。你可以在电子文档中找到它。

- Bundle modules with electron app:

- 使用电子应用捆绑模块:

I suppose you want to have your app as a stand-alone executable without requiring users to install electron on their machines. If so, I can suggest using electron-packager.

我想您希望将您的应用程序作为独立的可执行文件,而无需用户在其计算机上安装电子邮件。如果是这样,我可以建议使用电子包装器。

#1


9  

I did find a solution to this, but I have no idea if this is considered best practice. I couldn't find any good documentation for including 3rd party precompiled binaries, so I just fiddled with it until it finally worked with my ffmpeg binary. Here's what I did (starting with the electron quick start, node.js v6):

我确实找到了解决方案,但我不知道这是否是最佳实践。我找不到包含第三方预编译二进制文件的任何好文档,所以我只是摆弄它直到最终使用我的ffmpeg二进制文件。这就是我所做的(从电子快速启动开始,node.js v6):

Mac OS X method

Mac OS X方法

From the app directory I ran the following commands in Terminal to include the ffmpeg binary as a module:

在app目录中,我在Terminal中运行以下命令,将ffmpeg二进制文件包含为模块:

mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
cd node_modules/.bin
ln -s ../ffmpeg/ffmpeg ffmpeg

(replace /usr/local/bin/ffmpeg with your current binary path, download it from here) Placing the link allowed electron-packager to include the binary I saved to node_modules/ffmpeg/.

(将/ usr / local / bin / ffmpeg替换为您当前的二进制路径,从此处下载)放置链接允许electron-packager包含我保存到node_modules / ffmpeg /的二进制文件。

Then to get the bundled app path (so that I could use an absolute path for my binary... relative paths didn't seem to work no matter what I did) I installed the npm package app-root-dir by running the following command:

然后获取捆绑的应用程序路径(这样我可以使用我的二进制的绝对路径...无论我做什么,相对路径似乎都不起作用)我通过运行以下命令安装了npm包app-root-dir命令:

npm i -S app-root-dir

Now that I had the root app directory, I just append the subfolder for my binary and spawned from there. This is the code that I placed in renderer.js:.

现在我有了根应用程序目录,我只是为我的二进制文件附加子文件夹并从那里生成。这是我放在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}` );
    });

Windows Method

Windows方法

  1. Open your electron base folder (electron-quick-start is the default name), then go into the node_modules folder. Create a folder there called ffmpeg, and copy your static binary into this directory. Note: it must be the static version of your binary, for ffmpeg I grabbed the latest Windows build here.
  2. 打开电子基础文件夹(electron-quick-start是默认名称),然后进入node_modules文件夹。创建一个名为ffmpeg的文件夹,并将静态二进制文件复制到此目录中。注意:它必须是二进制文件的静态版本,对于ffmpeg,我在这里抓住了最新的Windows版本。
  3. To get the bundled app path (so that I could use an absolute path for my binary... relative paths didn't seem to work no matter what I did) I installed the npm package app-root-dir by running the following command from a command prompt in my app directory:

    要获取捆绑的应用程序路径(以便我可以使用绝对路径来实现我的二进制...相对路径似乎无论我做了什么都没有用)我通过运行以下命令安装了npm包app-root-dir从我的app目录中的命令提示符:

     npm i -S app-root-dir
    
  4. Within your node_modules folder, navigate to the .bin subfolder. You need to create a couple of text files here to tell node to include the binary exe file you just copied. Use your favorite text editor and create two files, one named ffmpeg with the following contents:

    在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
    

    And the the second text file, named ffmpeg.cmd:

    第二个文本文件名为ffmpeg.cmd:

    @IF EXIST "%~dp0\node.exe" (
     "%~dp0\node.exe"  "%~dp0\..\ffmpeg\ffmpeg" %*
    ) ELSE (
       @SETLOCAL
     @SET PATHEXT=%PATHEXT:;.JS;=;%
     node  "%~dp0\..\ffmpeg\ffmpeg" %*
    )
    

Next you can run ffmpeg in your Windows electron distribution (in renderer.js) as follows (I'm using the app-root-dir node module as well). Note the quotes added to the binary path, if your app is installed to a directory with spaces (eg C:\Program Files\YourApp) it won't work without these.

接下来,您可以在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}` );
 });

#2


8  

Here's another method, tested with Mac and Windows so far. Requires 'app-root-dir' package, doesn't require adding anything manually to node_modules dir.

这是另一种方法,到目前为止用Mac和Windows测试过。需要'app-root-dir'包,不需要手动向node_modules dir添加任何内容。

  1. Put your files under resources/$os/, where $os is either "mac", "linux", or "win". The build process will copy files from those directories as per build target OS.

    把你的文件放在resources / $ os /下,其中$ os是“mac”,“linux”或“win”。构建过程将根据构建目标OS从这些目录中复制文件。

  2. Put extraFiles option in your build configs as follows:

    在构建配置中添加extraFiles选项,如下所示:

package.json

的package.json

  "build": {
    "extraFiles": [
      {
        "from": "resources/${os}",
        "to": "Resources/bin",
        "filter": ["**/*"]
      }
    ],
  1. Use something like this to determine the current platform.
  2. 使用这样的东西来确定当前的平台。

get-platform.js

得到-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';
  }
};
  1. Call the executable from your app depending on env and OS. Here I am assuming built versions are in production mode and source versions in other modes, but you can create your own calling logic.
  2. 根据env和OS调用应用程序中的可执行文件。这里我假设构建版本处于生产模式,源代码版本处于其他模式,但您可以创建自己的调用逻辑。
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
});

I think I was using electron-builder as base, the env file generation comes with it. Basically it's just a JSON config file.

我认为我使用电子生成器作为基础,env文件生成随之而来。基本上它只是一个JSON配置文件。

#3


6  

tl;dr:

TL;博士:

yes you can! but it requires you to write your own self-contained addon which does not make any assumptions on system libraries. Moreover in some cases you have to make sure that your addon is compiled for the desired OS.

是的你可以!但是它要求你编写自己独立的插件,它不对系统库做任何假设。此外,在某些情况下,您必须确保为所需的操作系统编译插件。


Lets break this question in several parts:

让我们分几个部分来解决这个问题:

- Addons (Native modules):

- 插件(原生模块):

Addons are dynamically linked shared objects.

插件是动态链接的共享对象。

In other words you can just write your own addon with no dependency on system wide libraries (e.g. by statically linking required modules) containing all the code you need.

换句话说,您可以编写自己的插件,而不依赖于系统范围的库(例如,通过静态链接所需的模块),其中包含您需要的所有代码。

You have to consider that such approach is OS-specific, meaning that you need to compile your addon for each OS that you want to support! (depending on what other libraries you may use)

您必须考虑这种方法是特定于操作系统的,这意味着您需要为每个要支持的操作系统编译插件! (取决于您可能使用的其他库)

- Native modules for electron:

- 电子原生模块:

The native Node modules are supported by Electron, but since Electron is using a different V8 version from official Node, you have to manually specify the location of Electron's headers when building native modules

Electron支持本机节点模块,但由于Electron使用的是官方节点不同的V8版本,因此在构建本机模块时必须手动指定Electron标头的位置

This means that a native module which has been built against node headers must be rebuilt to be used inside electron. You can find how in electron docs.

这意味着必须重建一个针对节点头构建的本机模块,以便在电子内部使用。你可以在电子文档中找到它。

- Bundle modules with electron app:

- 使用电子应用捆绑模块:

I suppose you want to have your app as a stand-alone executable without requiring users to install electron on their machines. If so, I can suggest using electron-packager.

我想您希望将您的应用程序作为独立的可执行文件,而无需用户在其计算机上安装电子邮件。如果是这样,我可以建议使用电子包装器。