How to serve a static folder in Electron as it does in Express?


#1

We use ReactJs to build our app, when using React Offical build code, it returns a build stcuture like this:

The guide says you can drop the files in the build folder to any static server and it will work.
I did this to a Express 4.x app, it really works.

But when I did this to Electron app, it failed. Because when index.html and static folder are in the electron root folder, inside the index.html, it refers js or css file using directory like this:
<script type="text/javascript" src="/static/js/main.52a1ed14.js"></script>

In this way, Electron will try to look for the js file in following path and get no file found error, like this:

I have tried manully change the script src from “/static/js/xx.js” to “./static/js/xx.js” and “static/js/xx.js” (Please pay attention to the start of the src). Both work perfectly in Electron.

We really don’t want to touch the build process in order to avoid unneccessary potential problems.
My question is, how can I make Electron look for static files in certain folder like Express does. Or, is there anyway to make electron work leaving the default src unchanged?
Thanks.


How to exactly use Protocol Module?
#2

I don’t know ReactJS. Do you have the ability to insert arbitrary JavaScript in your index.html? If so, you could add a bit of code that corrects the path for you.


#3

Yeah, I have control on all the files. But the problem is we may have more than hundreds of files refering others in this way. I don;t think it is a great idea to change the src in this way


#4

You can register a custom protocol that resolves those URLs the way you want.


#5

Thank you very much. Looks like it is a elegant solution.

However, the docs are somewhat unclear. I tried the demo code but nothing happened. I don’t quite understand how exactly to use this protocol module to solve my problem.

Could you please share more thoughts?

For example:
Which method should I use? is this: protocol.registerFileProtocol(scheme, handler[, completion])
How to write the handler code?

Thank you again,
So sorry to trouble you with this.


#6

The handler function you pass to registerFileProtocol() will be called with two arguments, a request object, and a callback. You need to take the request url and resolve it to a path on disk, then pass the path to the callback. You may also need to call registerStandardSchemes. Now, for Electron to actually use your custom scheme you need to load your main page using browserWindow.loadURL('myscheme://index.html'), that way a url such as /static/js/main.52a1ed14.js will be passed to your custom protocol handler as myscheme://static/js/main.52a1ed14.js. Take a look at the actual implementation of the atom scheme in the Atom editor for reference.


#7

Thank you very much, I will look at the source code.


#8

I tried to load index.html via browserWindow.loadUrl() with my custom scheme that points to app root: approot://index.html.

First thing that broke were any custom params eg. approot://index.html?dev=true did not load index.html left window blank. Same with # params. I used protocol.registerFileProtocol() maybe that was the issue?

But I didn’t need those params, but the main problem was that changing protocol screwed requiring of modules from node_modules.

So eg. require('babel-register') said Cannot find module..

Any clue on that?


#9

I tried the registerFileProtocol() approach and had a lot of problems with it. Specifically, window.URL.createObjectURL(blob) was crashing the renderer. A better approach when using ReactJS bootstrapped with create-react-app is to set the following in the ReactJS app’s package.json:

{
  "homepage": "./"
}

This way, the generated files will use relative paths instead.


#10

I found this solution for whomever it may concern:
The WEB_FOLDER contains the index.html and all other relative referenced content.
You can set it to any empty string if your index file isn’t located inside a sub folder (web in this example)

function createWindow() {
const WEB_FOLDER = ‘web’;
const PROTOCOL = ‘file’;

electron.protocol.interceptFileProtocol(PROTOCOL, (request, callback) => {
// // Strip protocol
let url = request.url.substr(PROTOCOL.length + 1);

  // Build complete path for node require function
  url = path.join(__dirname, WEB_FOLDER, url);
  // Replace backslashes by forward slashes (windows)
  // url = url.replace(/\\/g, '/');
  url = path.normalize(url);
  console.log(url);
  callback({path: url});

});

// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false
}
});

// and load the index.html of the app.
mainWindow.loadURL(url.format({
pathname: ‘index.html’,
protocol: PROTOCOL + ‘:’,
slashes: true
}));