I am trying to execute a binary file in linux when an electron app is started. In development mode, everything is working properly, but when I build the app binary file (which is part of the app) is not executed.
Here is the code where I`m executing the binary file:
const { spawn, exec } = require('child_process');
const startServer = () => {
const ls = exec('./binary');
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
};
function createWindow() {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
icon: './electronJs.png',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
mainWindow.loadURL(
url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true,
})
);
}
app.whenReady().then(() => {
createWindow();
startServer();
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
Directory path ./ is relative to generated executable file once packaged.
You should use __dirname to fully qualify your binary path and make it relative to the calling file.
const path = require('path')
const myexefilepath = path.join(__dirname, 'binary')
...
const ls = exec(myexefilepath);
If you use asar file format to package your app, previous solution won't work.
Your options then are:
Copy binary to the same folder where the generated executable file is.
Use app.getPath( name ) to get a path to a special directory where you can put your binary. My choice being name: userData
Related
I'm building an app with electron react and MySQL, I'm stuck in preload script where i want to make my db instance available in render-er process, i got the following error
Error: module not found: ./config/db in console.
this happening when i try to require a module inside preload script.
const { app, BrowserWindow } = require("electron");
const path = require("path");
const isDev = require("electron-is-dev");
const dotenv = require("dotenv");
//load .env
dotenv.config();
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
title: "Electron",
minWidth: 800,
minHeight: 600,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
devTools: isDev,
},
});
//get url dependig on envirement (dev/prod)
const url = isDev
? `http://localhost:${process.env.PORT}/`
: `file://${path.join(__dirname, "../../dist/react/index.html")}`;
// load the url
mainWindow.loadURL(url);
// Open the DevTools.
isDev && mainWindow.webContents.openDevTools();
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow();
app.on("activate", function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on("window-all-closed", function () {
if (process.platform !== "darwin") app.quit();
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
require("./handlers");
preload
const { contextBridge, ipcRenderer } = require("electron");
const { db } = require("./config/db");
contextBridge.exposeInMainWorld("mainApi", {
db,
});
Since Electron v20.0.0, the sandbox parameter defaults to true (according to the list of Breaking Changes)
One of the side effects of the sandbox attribute is that it can only require a few things:
A require function similar to Node's require module is exposed, but can only import a subset of Electron and Node's built-in modules:
electron (only renderer process modules)
events
timers
url
To disable sandboxing, just add sandbox: false to your webPreferences on window creation:
// ...
// Create the browser window.
const mainWindow = new BrowserWindow({
title: "Electron",
minWidth: 800,
minHeight: 600,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
devTools: isDev,
sandbox: false, // fixes require() in preloader
},
});
// ...
My react web app runs fine in development mode, it also works fine when I host the build version from express server. But when I try to render it from electron js it does not render anything.
In console, it says Failed to load resource: net::ERR_FILE_NOT_FOUND main.8d9a4060.chunk.css:1 Failed to load resource: net::ERR_FILE_NOT_FOUND and I cant understand what is causing the issue.
my electron code is like this:-
const { app, BrowserWindow } = require('electron');
const path = require('path');
if (require('electron-squirrel-startup')) {
app.quit();
}
const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
});
mainWindow.loadFile(path.join(__dirname, 'build', 'index.html'));
mainWindow.webContents.openDevTools();
};
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
try 'loadURL' instead 'loadFile'
https://www.electronjs.org/docs/api/browser-window
This error occurs seldom in the app when it loads (Not all the time). The index.js is the main file ,selected in package.json, and the script.js is the file connected to main html file of the electron app.
index.js
const {app, BrowserWindow} = require('electron');
const path = require('path');
const url = require('url');
let window;
var APP_DIR = '/app/';
var IMG_DIR = '/images/';
function createWindow() {
window = new BrowserWindow({
webPreferences: { nodeIntegration: true },
width:610,
height:679,
icon: path.join(__dirname, APP_DIR, IMG_DIR, 'icon.png'),
frame: false,
resizable: false,
fullscreenable: false
});
window.loadURL(url.format({
pathname: path.join(__dirname, APP_DIR, 'index.html'),
protocol: 'file:',
slashes: true
}));
}
app.on('ready', createWindow);
script.js (where the error occurs)
var {BrowserWindow} = require('electron').remote;
BrowserWindow.getFocusedWindow().on('blur', function() {
windowBlurHandler(); //a function
});
How can I fix it?
Function BrowserWindow.getFocusedWindow() returns null when all windows are blurred. You are getting the error because listeners cannot be registered on null.
Try something like this instead:
const mainWindow = require('electron').remote.getCurrentWindow()
mainWindow.on('blur', function() {
windowBlurHandler()
})
In an Angular2 CLI project, i finnaly implemented this upload button from Vaadin. The button UI works, but i don't know how to actually make it upload a file anywhere.
I keep finding solutions about express server that listens for file uploads, multer or node server, and i really have no idea how to write such a server, where to put it, how to start it, how to access it, etc.. I figured that something as trivial as file upload should be easier to achieve, but it seems is not.
What is a simple solution to implement along side Angular2 in order make the button actually upload files somewhere so i can download them later?
Found the solution in ng2-uploader repo and adapted to work with Vaadin Upload.
component.html
<div *ngIf="newName.valid">
<vaadin-upload
target="http://localhost:10050/upload"
</vaadin-upload>
</div>
server.js
'use strict';
const Hapi = require('hapi');
const Inert = require('inert');
const Md5 = require('md5');
const Multiparty = require('multiparty');
const fs = require('fs');
const path = require('path');
const server = new Hapi.Server();
server.connection({ port: 10050, routes: { cors: true } });
server.register(Inert, (err) => {});
const upload = {
payload: {
maxBytes: 209715200,
output: 'stream',
parse: false
},
handler: (request, reply) => {
const form = new Multiparty.Form();
form.parse(request.payload, (err, fields, files) => {
if (err) {
return reply({status: false, msg: err});
}
let responseData = [];
files.file.forEach((file) => {
let fileData = fs.readFileSync(file.path);
const originalName = file.originalFilename;
const generatedName = Md5(new Date().toString() +
originalName) + path.extname(originalName);
const filePath = path.resolve(__dirname, 'uploads',
originalName);
fs.writeFileSync(filePath, fileData);
const data = {
originalName: originalName,
generatedName: generatedName
};
responseData.push(data);
});
reply({status: true, data: responseData});
});
}
};
const uploads = {
handler: {
directory: {
path: path.resolve(__dirname, 'uploads')
}
}
};
server.route([
{ method: 'POST', path: '/upload', config: upload },
{ method: 'GET', path: '/uploads/{path*}', config: uploads }
]);
server.start(() => {
console.log('Upload server running at', server.info.uri);
});
And a bonus for those who need server.js running at startup this is an awesome solution tested and working.
I am working on a project where I need to build a desktop app in Electron. The majority of functionality will be built in React, but there will be a part where we need to integrate a 3rd party static HTML magazine. I need some advice on how to do this. I am building a proof of concept app currently and I have based it on this https://github.com/chentsulin/electron-react-boilerplate
how would I add that on /static/ I server static HTML files. I know I could do it in express, but I really don't want to include the entire express framework just for serving static files.
I was looking at this https://www.npmjs.com/package/serve-static but have no Idea how to integrate it in my react app and bundle it into electron app.
I found another solution without using express or serve-static, we only
need to cusomize Electron built-in interceptFileProtocol() to serve static contents.
Code:(main.js)
(I use the electron-quick-start as Electron template)
function createWindow () {
window = new BrowserWindow({ width: 800, height: 600 })
window.loadURL(url.format({
pathname: 'index.html', /* Attention here: origin is path.join(__dirname, 'index.html') */
protocol: 'file',
slashes: true
}))
window.on('closed', () => {
window = null
})
}
app.on('ready', () => {
protocol.interceptFileProtocol('file', (request, callback) => {
const url = request.url.substr(7) /* all urls start with 'file://' */
callback({ path: path.normalize(`${__dirname}/${url}`)})
}, (err) => {
if (err) console.error('Failed to register protocol')
})
createWindow()
})
Reference:
protocol.interceptFileProtocol()
Explaination:
Normally, if you run React app as a normal website, all static contents should be served by HTTP [GET] method. Though they use relative paths, your HTTP server will handle the path parsing work.
However, when running under Electron, things change.
Your static contents usually use relative path like ./picture.jpg, Electron will use file protocol instead of HTTP protocol and find the file under root path like C://.//. So static contents like ./picture.jpg won't be loaded correctly.
By customizing interceptFileProtocol(), all static contents' requests will be pointed to your working directory instead of Windows(or other OS) root.
Finally, I'm not sure whether it's a good solution for all Electron projects, but if you already have a React project (or some other SPA) and want to wrap it with Electron, this solution would be fine to use.
As an addition to the great answer from #yeze322 above, here a working sample for all not so familiar with node and electron (like me). It took me some time to find out the correct require statements.
main.js (code from #yeze322 plus required imports)
const { app, BrowserWindow, protocol } = require('electron')
const path = require('path')
const url = require('url')
let mainWindow
function createWindow() {
mainWindow = new BrowserWindow({ width: 800, height: 600 })
mainWindow.loadURL(url.format({
pathname: 'index.html', /* Attention here: origin is path.join(__dirname, 'index.html') */
protocol: 'file',
slashes: true
}))
mainWindow.on('closed', function () {
mainWindow = null
})
}
app.on('ready', () => {
protocol.interceptFileProtocol('file', (request, callback) => {
const url = request.url.substr(7) /* all urls start with 'file://' */
callback({ path: path.normalize(`${__dirname}/${url}`) })
}, (err) => {
if (err) console.error('Failed to register protocol')
})
createWindow()
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function () {
if (mainWindow === null) {
createWindow()
}
})
In your main file you have
const app = require("app")
app.on("ready", () => {
...
Here you can start the server like you would do in node.js
const serveStatic = require('serve-static')
// or
const express = require('express')
...
}
Putting 3rd patry resources in the resources directory can solve the problem