Provide an example of serving static files
See original GitHub issuePreflight Checklist
- I have read the contribution documentation for this project.
- I agree to follow the code of conduct that this project follows, as appropriate.
- I have searched the issue tracker for a feature request that matches the one I want to file, without success.
Problem Description
I’m having trouble getting images to load in Electron consistently. I’m using Electron Forge with the webpack template https://www.electronforge.io/templates/webpack-template
My src
directory looks like this:
├── images
│ └── black.png
├── index.css
├── index.html
├── index.js
├── main.js
└── renderer.js
My HTML code looks like this:
<img src="images/black.png">
I’m using copy-webpack-plugin
to copy the images
directory.
When running in development (npm run start
) the root of the dev server is .webpack/renderer
so the image loads. When running in production (npm run package
) the HTML file is being opened from the file system so the image tag is trying to access .webpack/renderer/main_window/images
which is the wrong location and it doesn’t load.
I’ve seen the recommendation to import the image into renderer.js
but I have a few thousand images. Should I do it like this?
import './images/1.png';
import './images/2.png';
import './images/3.png';
// ...
import './images/1000.png';
Is there a way to programmatically import? Something like:
let imageMap = {};
for (let i of Iter.range(1, 1001)) {
let image = import(`./images/${i}.png`);
imageMap[i] = image;
}
Then how would I refer to it in HTML? Can it be done without DOM editing code?
I prefer not to import my images into my JavaScript file and run them through webpack loaders. I just want to reference static files from my HTML code in a way that works in both development and production.
I also have a 5MB JSON file that I need to access using fetch()
. I tried to import this through a loader but the build process took more than 5 minutes and I killed it.
Proposed Solution
I can think of a few ways to solve this:
- The webpack config needs to know via some kind of environment variable or flag if it is running in development or production and change the copy-webpack-plugin’s “to” path.
- Change the development server to run so its root is
.webpack/renderer/main_window
- In production run a server instead of loading from the file system
I was able to implement the last option by running a static Express server in production serving the renderer
directory. I use absolute paths (/images/foo.png) for image tags and can access my static files.
webpack.renderer.config.js
const path = require('path');
const rules = require('./webpack.rules');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const assets = ['data', 'images'];
const copyPlugins = assets.map(asset => {
return new CopyWebpackPlugin([
{
from: path.resolve(__dirname, 'src', asset),
to: asset
}
]);
});
module.exports = {
module: {
rules: [
...rules,
],
},
plugins: [
...copyPlugins,
]
};
main.js
import { app, BrowserWindow } from 'electron';
import path from 'path';
import express from 'express';
function isDebug() {
return process.env.npm_lifecycle_event === 'start';
}
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
app.quit();
}
const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1200,
height: 740,
});
if (isDebug()) {
// Create the browser window.
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
mainWindow.webContents.openDevTools();
} else {
const exApp = express();
exApp.use(express.static(path.resolve(__dirname, '..', 'renderer')));
const server = exApp.listen(0, () => {
console.log(`port is ${server.address().port}`);
mainWindow.loadURL(`http://localhost:${server.address().port}/main_window/`);
});
}
};
// 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.on('ready', createWindow);
Additional Information
I’ve seen a solution expressed in these links but I could not get it to work in both development and production.
https://github.com/electron-userland/electron-forge/issues/1196
https://github.com/electron-userland/electron-forge/issues/941
Conclusion
Loading static files is a basic part of making web pages and should be a part of the project template.
Please implement some kind of recommendation or code sample in the docs for static file serving. This is not the first ticket talking about this. People are spending huge amounts of time on it. This is something that should be simple but I’ve spent hours trying to figure it out.
I know there is an example of importing a CSS file into a JavaScript file but that it a unusable approach when you have thousands of large files to the point that the webpack dev server cannot even boot.
There is not an explanation of why the development system runs from a server and the production one runs from a file other than I suppose that the dev serve gives reloading ability. This mismatch is the root of the problem everyone is having. Running a server in production is the most reasonable way to make these the same.
And I just today saw a new ticket marking this as a bug https://github.com/electron-userland/electron-forge/issues/1591.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:27
- Comments:14 (1 by maintainers)
Top GitHub Comments
There is a solution if you are not using a server:
webpack.render.config.js
You need to register own file protocol to handle custom requests for static files. Do it in window creation handler.
main.js
Then you can use it in html like this:
@nikitasnv I try use this to solve my problem with the static assets but when i use this with copy-webpack-plugin throw me a error.
i replace this for
and this work for me.
Tested on: copy-webpack-plugin@6.4.1