I have a desktop app I'm building with electron. The UI hits a flask server I have running on local host and tries to download a file. I cannot figure out why the download always immediately enters an "interrupted" state. The backend server is never even hit so I suspect the problem has something to do with my electron configuration.
Below is a minimal working example that triggers the problem. Most of the code is boilerplate lifted from other sources.
main.js
const {app, BrowserWindow, ipcMain, shell} = require('electron')
const path = require('path')
let mainWindow;
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
mainWindow.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
ipcMain.on('download-file', async (event, properties) => {
console.log(`Downloading ${properties.link}`)
mainWindow.webContents.downloadURL(`${properties.link}`)
mainWindow.webContents.session.on('will-download', (event, item, webContents) => {
item.setSavePath(`${path.resolve(app.getPath("appData"), properties.filename)}`)
item.on('updated', (event, state) => {
if (state === 'interrupted') {
console.log('Download interrupted but can be resumed')
} else if (state === 'progressing') {
if (item.isPaused()) {
console.log("Item is paused")
} else {
console.log(`Recieved bytes ${item.getReceivedBytes()}`)
}
}
})
item.once('done', (event, state) => {
if (state === 'completed') {
console.log("Downloaded successfully")
shell.openItem(item.getSavePath())
} else {
console.log(`Download failed ${state}`)
}
})
});
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
preload.js
const {ipcRenderer, contextBridge} = require('electron')
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
contextBridge.exposeInMainWorld('electron', {
download() {
ipcRenderer.send('download-file', {link: "http://127.0.0.1:5000/file/test.txt", filename: "test.txt"})
}
})
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<link href="./styles.css" rel="stylesheet">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>,
Chromium <span id="chrome-version"></span>,
and Electron <span id="electron-version"></span>.
<input type="button" value="Download" id="downloadButton">
<script src="./renderer.js"></script>
</body>
</html>
renderer.js
const download = window.electron.download
document.getElementById("downloadButton").onclick = download;
EDIT: Of course it turns out the backend was the problem. The file being requested didn't exist. Ignore this question