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

b1payxdu  于 12个月前  发布在  Electron
关注(0)|答案(6)|浏览(169)

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

deikduxw

deikduxw1#

这是另一种方法,到目前为止已经在Mac和Windows上测试过了。需要'app-root-dir'包,不需要手动添加任何东西到node_modules目录。
1.将文件放在 resources/$os/下,其中 $os“mac”“Linux”“win”。构建过程将根据构建目标操作系统从这些目录复制文件。
1.将extraFiles选项放入构建包中,如下所示:

  • package.json*
"build": {
    "extraFiles": [
      {
        "from": "resources/${os}",
        "to": "Resources/bin",
        "filter": ["**/*"]
      }
    ],

字符串
1.使用类似的东西来确定当前平台。

  • get-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.根据环境和操作系统,从应用调用可执行文件。这里我假设构建版本处于生产模式,源版本处于其他模式,但您可以创建自己的调用逻辑。

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
});


我想我是用electron-builder作为基础的,env文件的生成是附带的。基本上它只是一个JSON配置文件。

uqjltbpv

uqjltbpv2#

  • 请参见下面的UPDATE(此方法目前不理想)。*

我确实找到了一个解决方案,但我不知道这是否被认为是最佳实践。我找不到任何好的文档来包含第三方预编译的二进制文件,所以我只是摆弄它,直到它最终与我的ffmpeg二进制文件一起工作。下面是我所做的(从electron快速入门,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

字符串
(将/usr/local/bin/ffmpeg替换为您当前的二进制路径,从这里下载)放置链接允许电子打包器包含我保存到node_modules/ffmpeg/的二进制文件。
然后,为了获得捆绑的应用程序路径(这样我就可以为我的二进制文件使用绝对路径.

npm i -S app-root-dir


现在我已经有了根应用目录,我只是附加了我的二进制文件的子文件夹,并从那里产生。

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方法**

1.打开你的electron基本文件夹(electron-quick-start是默认名称),然后进入node_modules文件夹。在那里创建一个名为ffmpeg的文件夹,并将你的静态二进制文件复制到这个目录中。注意:它必须是你的静态二进制文件的版本,对于ffmpeg我抓取了最新的Windows build here
1.为了获取绑定的应用程序路径(这样我就可以为我的二进制文件使用绝对路径.无论我做什么,相对路径似乎都不起作用),我通过在应用程序目录中的命令提示符下运行以下命令安装了npm包app-root-dir:

npm i -S app-root-dir


1.在node_modules文件夹中,导航到. bin子文件夹。您需要在这里创建几个文本文件,以告诉node包含刚刚复制的二进制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


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

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


接下来,你可以在你的Windows电子发行版中运行ffmpeg(在renderer.js中),如下所示(我也使用了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}` );
 });

    • 更新:统一简单方法**

好吧,随着时间的推移和Node的更新,这个方法不再是包含预编译二进制文件的最简单方法。它仍然有效,但是当npm install运行时,node_modules下的二进制文件夹将被删除,并且必须再次替换。下面的方法适用于Node v12。
这种新方法消除了符号链接的需要,在Mac和Windows上的工作原理类似。
1.您仍然需要appRootDir:npm i -S app-root-dir
1.在应用的根目录下创建一个名为bin的文件夹,并将预编译的静态二进制文件放在这里,我使用ffmpeg作为示例。
1.在渲染器脚本中使用以下代码:

const appRootDir = require('app-root-dir').get();
const ffmpegpath = appRootDir + '/bin/ffmpeg';
const spawn = require( 'child_process' ).spawn;
const child = spawn( ffmpegpath, ['-i', inputfile, 'out.mp4']);  //add whatever switches you need here, test on command line first
child.stdout.on( 'data', data => {
    console.log( `stdout: ${data}` );
});
child.stderr.on( 'data', data => {
    console.log( `stderr: ${data}` );
});

qrjkbowd

qrjkbowd3#

上面的答案帮助我理解了如何做到这一点,但是有一种更有效的方法来分发二进制文件。
从鹤贺的回答中得到灵感,我修改了代码:
注:根据需要替换或添加操作系统路径。

更新-2020年12月4日

此答案已更新。您可以在此答案的底部找到以前版本的代码。

  • 下载必要的软件包。
yarn add electron-root-path electron-is-packaged
    
    # or 
    
    npm i electron-root-path electron-is-packaged

字符串

  • 创建目录 ./resources/mac/bin
  • 将二进制文件放入此文件夹
  • 创建一个文件 ./app/binaries.js 并粘贴以下代码:
import path from 'path';
    import { rootPath as root } from 'electron-root-path';
    import { isPackaged } from 'electron-is-packaged';
    import { getPlatform } from './getPlatform';
    
    const IS_PROD = process.env.NODE_ENV === 'production';
    
    const binariesPath =
      IS_PROD && isPackaged // the path to a bundled electron app.
        ? path.join(root, './Contents', './Resources', './bin')
        : path.join(root, './build', 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, {});

为什么这是一个更好的方法?

  • Tsuriga的解决方案没有充分解决生产环境(env=生产)中的 * 构建 * 或 * 预打包 * 版本。它只关注开发和后打包版本。

上一个应答

  • 避免使用electron.remote,因为它已被弃用。
  • 使用app.getAppPath可能会导致主进程出错。
  • ./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'));

avwztpqn

avwztpqn4#

tl;dr:

是的你可以!但它要求你编写自己的独立插件,而不对系统库做任何假设。此外,在某些情况下,你必须确保你的插件是为所需的操作系统编译的。
让我们把这个问题分成几个部分:

-Addons(本机模块)

插件是动态链接的共享对象。
换句话说,你可以编写自己的插件,而不依赖于系统范围的库(例如,通过静态链接所需的模块),其中包含所有你需要的代码。
你必须考虑到这种方法是特定于操作系统的,这意味着你需要为你想要支持的每个操作系统编译你的插件!(取决于你可能使用的其他库)

-Native modules表示电子

Electron支持本机Node模块,但由于Electron使用的是与官方Node不同的V8版本,因此在构建本机模块时必须手动指定Electron头文件的位置
这意味着一个已经针对节点头构建的本地模块必须重新构建才能在electron内部使用。你可以在electron文档中找到如何构建。

-将模块与电子app捆绑

我想你希望你的应用程序是一个独立的可执行程序,而不需要用户在他们的机器上安装electron,如果是这样的话,我建议使用electron-packager

nle07wnf

nle07wnf5#

遵循Ganesh的回答,这真的是一个很大的帮助,在我的情况下,在binaries.js中工作的是什么(对于Mac构建-没有测试Windows或Linux):

"use strict";
import path from "path";
import { app } from "electron";

const IS_PROD = process.env.NODE_ENV === "production";
const root = process.cwd();
const { isPackaged } = app;

const binariesPath =
  IS_PROD && isPackaged
    ? path.join(process.resourcesPath, "./bin")
    : path.join(root, "./external");

export const execPath = path.join(binariesPath, "./my_exec_name");

字符串
考虑到my_exec_name./external/bin文件夹中并复制到./Resources/bin中的应用程序包中。我没有使用get_platforms.js脚本(在我的情况下不需要)。app.getAppPath()在打包应用程序时生成崩溃。希望它能有所帮助。

ncgqoxb0

ncgqoxb06#

很大程度上基于Ganesh的答案,但有些简化。而且我使用的是Vue CLI Electron Builder Plugin,所以配置必须在一个稍微不同的地方。
1.创建一个resources目录。把你所有的文件都放在里面。
1.将此添加到vue.config.js

module.exports = {
  pluginOptions: {
    electronBuilder: {
      builderOptions: {
        ...
        "extraResources": [
          {
            "from": "resources",
            "to": ".",
            "filter": "**/*"
          }
        ],
        ...
      }
    }
  }
}

字符串
1.在src文件夹中创建一个名为resources.ts的文件,其中包含以下内容:

import path from 'path';
import { remote } from 'electron';

// Get the path that `extraResources` are sent to. This is `<app>/Resources`
// on macOS. remote.app.getAppPath() returns `<app>/Resources/app.asar` so
// we just get the parent directory. If the app is not packaged we just use
// `<current working directory>/resources`.
export const resourcesPath = remote.app.isPackaged ?
                             path.dirname(remote.app.getAppPath()) :
                             path.resolve('resources');


注意:我还没有在Windows/Linux上测试过,但是假设app.asar在这些平台上的resources目录中,它应该可以工作(我假设是这样)。
1.像这样使用它:

import { resourcesPath } from '../resources'; // Path to resources.ts

...
    loadFromFile(resourcesPath + '/your_file');

相关问题