Existe-t-il une bonne solution pour inclure des fichiers binaires pré-compilés tiers, comme imagemagick, dans une application électronique? il existe des modules node.js mais ils constituent tous des wrappers ou des liaisons natives aux bibliothèques installées à l'échelle du système. Je me demande s'il est possible de regrouper des fichiers binaires précompilés dans la distribution.
J'ai trouvé une solution à cela, mais je ne sais pas si cela est considéré comme une pratique exemplaire. Je ne pouvais trouver aucune bonne documentation pour inclure les fichiers binaires précompilés par une tierce partie, aussi je me suis contenté de la manipuler jusqu'à ce que tout fonctionne avec le fichier binaire ffmpeg. Voici ce que j'ai fait (en commençant par le démarrage rapide de l'électron, node.js v6):
Méthode Mac OS X
Depuis le répertoire de l'application, j'ai exécuté les commandes suivantes dans Terminal pour inclure le fichier binaire ffmpeg en tant que module:
mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
cd node_modules/.bin
ln -s ../ffmpeg/ffmpeg ffmpeg
(remplacez /usr/local/bin/ffmpeg
par votre chemin binaire actuel, téléchargez-le à partir d'ici) Placez le lien qui permettait à emballeur électronique d'inclure le binaire que j'avais enregistré dans node_modules/ffmpeg/
.
Ensuite, pour obtenir le chemin d’application fourni (pour pouvoir utiliser un chemin absolu pour mon fichier binaire ... les chemins relatifs ne semblent pas fonctionner, peu importe ce que je faisais), j’ai installé le paquet npm app-root-dir en lançant ce qui suit: commander:
npm i -S app-root-dir
Maintenant que j'ai le répertoire racine de l'application, je viens d'ajouter le sous-dossier de mon binaire et de le générer. C'est le code que j'ai placé dans 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}` );
});
Méthode Windows
Pour obtenir le chemin d'application fourni (afin que je puisse utiliser un chemin absolu pour mon binaire ... les chemins relatifs ne semblent pas fonctionner, peu importe ce que j'ai fait), j'ai installé le paquet npm app-root-dir en exécutant la commande suivante à partir d'une invite de commande dans mon répertoire d'applications:
npm i -S app-root-dir
Dans votre dossier node_modules, accédez au sous-dossier .bin. Vous devez créer ici quelques fichiers texte pour indiquer à noeud d’inclure le fichier exe binaire que vous venez de copier. Utilisez votre éditeur de texte préféré et créez deux fichiers, l'un nommé ffmpeg
avec le contenu suivant:
#!/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
Et le deuxième fichier texte, nommé ffmpeg.cmd
:
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\ffmpeg\ffmpeg" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\ffmpeg\ffmpeg" %*
)
Ensuite, vous pouvez exécuter ffmpeg dans votre distribution électronique Windows (dans renderer.js) comme suit (j'utilise également le module de nœud app-root-dir). Notez les guillemets ajoutés au chemin binaire. Si votre application est installée dans un répertoire avec des espaces (par exemple, C:\Program Files\YourApp
), elle ne fonctionnera pas sans ceux-ci.
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}` );
});
Voici une autre méthode, testée avec Mac et Windows jusqu'à présent. Requiert le package 'app-root-dir', sans rien ajouter manuellement à node_modules dir.
Placez vos fichiers sous resources/$ os /, où $ os est soit "mac", "linux" ou "win". Le processus de construction copie les fichiers de ces répertoires selon le système d’exploitation cible.
Mettez l'option extraFiles
dans vos configurations de construction comme suit:
package.json
"build": {
"extraFiles": [
{
"from": "resources/${os}",
"to": "Resources/bin",
"filter": ["**/*"]
}
],
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';
}
};
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
});
Je pense que j'utilisais electron-builder comme base, la génération du fichier env vient avec. Fondamentalement, c'est juste un fichier de configuration JSON.
tl; dr:
oui, vous pouvez! mais cela nécessite que vous écriviez votre propre addon autonome qui ne fait aucune supposition sur les bibliothèques système. De plus, dans certains cas, vous devez vous assurer que votre addon est compilé pour le système d'exploitation souhaité.
Permet de casser cette question en plusieurs parties:
- Addons (modules natifs):
Les addons sont des objets partagés liés dynamiquement.
En d'autres termes, vous pouvez simplement écrire votre propre addon sans aucune dépendance vis-à-vis des bibliothèques système (par exemple en liant statiquement les modules requis) contenant tout le code dont vous avez besoin.
Vous devez considérer que cette approche est spécifique à un système d'exploitation, ce qui signifie que vous devez compiler votre addon pour chaque système d'exploitation que vous souhaitez prendre en charge! (en fonction des autres bibliothèques que vous pouvez utiliser)
- Modules natifs pour electron:
Les modules de nœud natifs sont pris en charge par Electron, mais comme Electron utilise une version V8 différente de celle du nœud officiel, vous devez spécifier manuellement l'emplacement des en-têtes d'Electron lors de la création de modules natifs.
Cela signifie qu'un module natif qui a été construit avec les en-têtes de nœud doit être reconstruit pour être utilisé dans electron. Vous pouvez trouver comment dans les documents électroniques.
- Bundle de modules avec application électronique:
Je suppose que vous voulez avoir votre application en tant qu'exécutable autonome sans obliger les utilisateurs à installer electron sur leurs machines. Si c'est le cas, je peux suggérer d'utiliser electron-packager .
Les réponses ci-dessus m'ont aidé à comprendre comment cela se fait. Mais il existe un moyen beaucoup plus efficace de distribuer des fichiers binaires.
En prenant exemple sur la réponse de tsuriga , voici mon code:
Remarque: remplacez ou ajoutez le chemin OS en conséquence.
'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'));
'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'; } };
"build": { .... "extraFiles": [ { "from": "resources/mac/bin", "to": "Resources/bin", "filter": [ "**/*" ] } ], .... },
import { execPath } from './binaries'; #your program code: var command = spawn(execPath, arg, {});
Pourquoi c'est mieux?
Les réponses ci-dessus nécessitent un package supplémentaire appelé app-root-dir
la réponse de tsuriga ne gère pas correctement les versions (env = production) build ou préemballées. Il/elle n'a pris en charge que les versions de développement et de post-emballage.
suivre les réponses de Ganesh, ce qui m'a vraiment été d'une grande aide, dans mon cas, ce qui fonctionnait dans binaries.js (pour une version Mac - n'a pas testé Windows ni Linux) était:
"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");
Considérant que my_exec_name
était dans le dossier ./external/bin
et copié dans le package d'application dans ./Resources/bin
. Je n'ai pas utilisé le script get_platforms.js (inutile dans mon cas). app.getAppPath () était en train de générer un blocage lorsque l'application a été empaquetée . J'espère que cela pourra vous aider.