Electron File Download Manager

A simple and easy to use file download manager for Electron applications.
Designed in response to the many issues around electron-dl
and provides
a more robust and reliable solution for downloading files in Electron.
Use cases:
- Download files from a URL
- Get an id associated with the download to track it
- Optionally show a "Save As" dialog
- Get progress updates on the download
- Be able to cancel / pause / resume downloads
- Support multiple downloads at once
Electron 26.0.0 or later is required.
import { ElectronDownloadManager } from 'electron-dl-manager';
const manager = new ElectronDownloadManager();
const id = await manager.download({
window: browserWindowInstance,
url: 'https://example.com/file.zip',
saveDialogOptions: {
title: 'Save File',
},
callbacks: {
onDownloadStarted: async ({ id, item, webContents }) => {
},
onDownloadProgress: async (...) => {},
onDownloadCompleted: async (...) => {},
onDownloadCancelled: async (...) => {},
onDownloadInterrupted: async (...) => {},
onError: (err, data) => {},
}
});
manager.cancelDownload(id);
manager.pauseDownload(id);
manager.resumeDownload(id);
Table of contents
Installation
$ npm install electron-dl-manager
Getting started
You'll want to use electron-dl-manager
in the main process of your
Electron application where you will be handling the file downloads.
In this example, we use IPC handlers / invokers
to communicate between the main and renderer processes, but you can
use any IPC strategy you want.
import { ElectronDownloadManager } from 'electron-dl-manager';
import { ipcMain } from 'electron';
const manager = new ElectronDownloadManager();
ipcMain.handle('download-file', async (event, args) => {
const { url } = args;
let downloadId
const browserWindow = BrowserWindow.fromId(event.sender.id)
downloadId = await manager.download({
window: browserWindow,
url,
saveAsFilename: 'file.zip',
directory: '/directory/where/to/save',
saveDialogOptions: {
title: 'Save File',
},
callbacks: {
onDownloadStarted: async ({ id, item, resolvedFilename }) => {
browserWindow.webContents.invoke('download-started', {
id,
filename: resolvedFilename,
totalBytes: item.getTotalBytes(),
});
},
onDownloadProgress: async ({ id, item, percentCompleted }) => {
browserWindow.webContents.invoke('download-progress', {
id,
percentCompleted,
bytesReceived: item.getReceivedBytes(),
});
},
onDownloadCompleted: async ({ id, item }) => {
browserWindow.webContents.invoke('download-completed', {
id,
filePath: item.getSavePath(),
});
},
onError: (err, data) => {
}
}
});
manager.pauseDownload(downloadId);
});
API
Class: ElectronDownloadManager
Manages file downloads in an Electron application.
constructor()
constructor(params: DownloadManagerConstructorParams)
interface DownloadManagerConstructorParams {
debugLogger?: (message: string) => void
}
download()
Starts a file download. Returns the id
of the download.
download(params: DownloadParams): Promise<string>
Interface: DownloadParams
interface DownloadParams {
window: BrowserWindow
url: string
callbacks: DownloadManagerCallbacks
downloadURLOptions?: Electron.DownloadURLOptions
saveDialogOptions?: SaveDialogOptions
saveAsFilename?: string
directory?: string
overwrite?: boolean
}
Interface: DownloadManagerCallbacks
interface DownloadManagerCallbacks {
onDownloadStarted: (data: DownloadData) => void
onDownloadProgress: (data: DownloadData) => void
onDownloadCompleted: (data: DownloadData) => void
onDownloadCancelled: (data: DownloadData) => void
onDownloadInterrupted: (data: DownloadData) => void
onError: (error: Error, data?: DownloadData) => void
}
cancelDownload()
Cancels a download.
cancelDownload(id: string): void
pauseDownload()
Pauses a download.
pauseDownload(id: string): void
resumeDownload()
Resumes a download.
resumeDownload(id: string): void
getActiveDownloadCount()
Returns the number of active downloads.
getActiveDownloadCount(): number
getDownloadData()
Returns the download data for a download.
getDownloadData(id: string): DownloadData
Class: DownloadData
Data returned in the callbacks for a download.
Properties
class DownloadData {
id: string
item: DownloadItem
webContents: WebContents
event: Event
resolvedFilename: string
cancelledFromSaveAsDialog?: boolean
percentCompleted: number
downloadRateBytesPerSecond: number
estimatedTimeRemainingSeconds: number
interruptedVia?: 'in-progress' | 'completed'
}
Formatting download progress
You can use the libraries bytes
and dayjs
to format the download progress.
$ npm install bytes dayjs
$ npm install @types/bytes --save-dev
import bytes from 'bytes'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime';
import duration from 'dayjs/plugin/duration';
dayjs.extend(relativeTime);
dayjs.extend(duration);
const downloadData = manager.getDownloadData(id);
const formattedDownloadRate = bytes(downloadData.downloadRateBytesPerSecond, { unitSeparator: ' ' }) + '/s'
const formattedEstimatedTimeRemaining = dayjs.duration(downloadData.estimatedTimeRemainingSeconds, 'seconds').humanize(true)
isDownloadInProgress()
Returns true if the download is in progress.
isDownloadInProgress(): boolean
isDownloadPaused()
Returns true if the download is paused.
isDownloadPaused(): boolean
isDownloadResumable()
Returns true if the download is resumable.
isDownloadResumable(): boolean
isDownloadCancelled()
Returns true if the download is cancelled.
isDownloadCancelled(): boolean
isDownloadInterrupted()
Returns true if the download is interrupted.
isDownloadInterrupted(): boolean
isDownloadCompleted()
Returns true if the download is completed.
isDownloadCompleted(): boolean
Mock class
If you need to mock out ElectronDownloadManager
in your tests, you can use the ElectronDownloadManagerMock
class.
import { ElectronDownloadManagerMock } from 'electron-dl-manager'
FAQ
How do I capture if the download is invalid? onError()
is not being called.
Electron DownloadItem
doesn't provide an explicit way to capture errors for downloads in general:
https://www.electronjs.org/docs/latest/api/download-item#class-downloaditem
(It only has on('updated')
and on('done')
events, which this library uses for defining the callback handlers.)
What it does for invalid URLs, it will trigger the onDownloadCancelled()
callback.
const id = await manager.download({
window: mainWindow,
url: 'https://alkjsdflksjdflk.com/file.zip',
callbacks: {
onDownloadCancelled: async (...) => {
},
}
});
A better way to handle this is to check if the URL exists prior to the download yourself.
I couldn't find a library that I felt was reliable to include into this package,
so it's best you find a library that works for you:
GPT also suggests the following code (untested):
async function urlExists(url: string): Promise<boolean> {
try {
const response = await fetch(url, { method: 'HEAD' });
return response.ok;
} catch (error) {
return false;
}
}
const exists = await urlExists('https://example.com/file.jpg');
Acknowledgments
This code uses small portions from electron-dl
and is noted in the
code where it is used.
electron-dl
is licensed under the MIT License and is maintained by Sindre Sorhus sindresorhus@gmail.com (https://sindresorhus.com).