ElectronJS is an Open Source Framework used for building Cross-Platform native desktop applications using web technologies such as HTML, CSS, and JavaScript which are capable of running on Windows, macOS, and Linux operating systems. It combines the Chromium engine and NodeJS into a Single Runtime.
Any native desktop application should integrate itself with the System OS environment. The application should have the ability to interact with core OS functionalities such as the File System, System Tray, etc. Electron provides us with built-in dialog module to display the native System dialogs for interacting with files. This tutorial will use the instance method of the dialog module to demonstrate how to Save Files locally in Electron.
We assume you are familiar with the prerequisites as covered in the above-mentioned link. For Electron to work, node and npm need to be pre-installed in the system.
dialog Module: The dialog Module is part of the Main Process. To import and use the dialog Module in the Renderer Process, we will be using Electron remote module. For more details on the remote module, Refer this link.
- Project Structure:
Example: We will start by building the Electron Application for Saving files to local System by following the given steps.
- Step 1: Navigate to an Empty Directory to setup the project, and run the following command.
npm init
To generate the package.json file. Install Electron using npm if it is not installed.
npm install electron --save
This command will also create the package-lock.json file and install the required node_modules dependencies. Create the assets folder according to the project structure. We will save the new files to this folder from the native dialog.
package.json:{ "name": "electron-save", "version": "1.0.0", "description": "Save Files to local in Electron", "main": "main.js", "scripts": { "start": "electron ." }, "keywords": [ "electron" ], "author": "Radhesh Khanna", "license": "ISC", "dependencies": { "electron": "^8.2.5" } }
- Step 2: Create the main.js file according to the project structure. This file is the Main Process and acts as an entry point into the application. Copy the Boilerplate code for the main.js file as given in the following link. We have modified the code to suit our project needs.
main.js:
const { app, BrowserWindow } = require(
'electron'
)
function
createWindow () {
// Create the browser window.
const win =
new
BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration:
true
}
})
// Load the index.html of the app.
win.loadFile(
'src/index.html'
)
// Open the DevTools.
win.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.
// This method is equivalent to 'app.on('ready', function())'
app.whenReady().then(createWindow)
// Quit when all windows are closed.
app.on(
'window-all-closed'
, () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if
(process.platform !==
'darwin'
) {
app.quit()
}
})
app.on(
'activate'
, () => {
// 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()
}
})
// 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.
- Step 3: Create the index.html file and index.js file within the src directory. We will also copy the boilerplate code for the index.html file from the above-mentioned link. We have modified the code to suit our project needs.
index.html:
<!DOCTYPE html>
<
html
>
<
head
>
<
meta
charset
=
"UTF-8"
>
<
title
>Hello World!</
title
>
/security#csp-meta-tag -->
<
meta
http-equiv
=
"Content-Security-Policy"
content
=
"script-src 'self' 'unsafe-inline';"
/>
</
head
>
<
body
>
<
h1
>Hello World!</
h1
>
We are using node
<
script
>
document.write(process.versions.node)
</
script
>, Chrome
<
script
>
document.write(process.versions.chrome)
</
script
>, and Electron
<
script
>
document.write(process.versions.electron)
</
script
>.
<
br
><
br
>
<
button
id
=
"save"
>Save sample.txt to local System</
button
>
<!-- Adding Individual Renderer Process JS File -->
<
script
src
=
"index.js"
></
script
>
</
body
>
</
html
>
Output: At this point, our application is set up and we can launch the application to check the GUI Output. To launch the Electron Application, run the Command:
npm start
- Step 4: The Save sample.txt to local System button does not have any functionality associated with it yet.
index.js: Add the following snippet in that file.const electron = require(
'electron'
);
const path = require(
'path'
);
const fs = require(
'fs'
);
// Importing dialog module using remote
const dialog = electron.remote.dialog;
var
save = document.getElementById(
'save'
);
save.addEventListener(
'click'
, (event) => {
// Resolves to a Promise<Object>
dialog.showSaveDialog({
title:
'Select the File Path to save'
,
defaultPath: path.join(__dirname,
'../assets/sample.txt'
),
// defaultPath: path.join(__dirname, '../assets/'),
buttonLabel:
'Save'
,
// Restricting the user to only Text Files.
filters: [
{
name:
'Text Files'
,
extensions: [
'txt'
,
'docx'
]
}, ],
properties: []
}).then(file => {
// Stating whether dialog operation was cancelled or not.
console.log(file.canceled);
if
(!file.canceled) {
console.log(file.filePath.toString());
// Creating and Writing to the sample.txt file
fs.writeFile(file.filePath.toString(),
'This is a Sample File'
,
function
(err) {
if
(err)
throw
err;
console.log(
'Saved!'
);
});
}
}).
catch
(err => {
console.log(err)
});
});
The dialog.showSaveDialog(browserWindow, options) takes in the following parameters. For more detailed information on dialog.showSaveDialog() method, Refer this link.
- browserWindow: BrowserWindow (Optional) The BrowserWindow Instance. This argument allows the native dialog to attach itself to the parent window, making it a modal. A modal window is a child window that disables the parent window. If BrowserWindow Instance is not shown, dialog will not be attached to it. In such case It will be displayed as independent window. In the above code, the BrowserWindow instance is not being passed to the dialog, therefore the dialog opens as an independent window on clicking the Save sample.txt to local System button. For more detailed information on BrowserWindow Object and Modal window, Refer this link.
- options: Object It takes in the following parameters,
- title: String (Optional) The title to be displayed on the dialog window.
- defaultPath: String (Optional) The Absolute directory path, the absolute file path or the file name to be opened/used by default on clicking the Save sample.txt to local System button. In case, we specify the absolute directory path, the file Explorer will navigate to that directory but will not populate the File name: text field in the dialog window. In case, the absolute file path is specified along with the file name and extension, the File name: text field will be auto-populated as shown in the code. In both of these cases the file path and the file name can be changed from within the dialog window.
- buttonLabel: String (Optional) Custom label for the confirmation Button. If empty, the default label will be used. In the above code it is defined as Save.
- message: String (Optional) This parameter is supported in macOS only. This is used to display the custom message above the text fields.
- nameFieldLabel: String (Optional) This parameter is supported in macOS only. It defines a custom label for the text displayed in front of the File name: text field.
- showsTagField: Boolean (Optional) This parameter is supported in macOS only. It shows the tags input box to assign custom tags to files. Default value is true.
- securityScopedBookmarks: Boolean (Optional) This parameter is supported in macOS only. This parameter is used to create security scoped bookmark when packaged for the Mac App Store. If this option is set to true and the file does not exist then a blank file will be created at the chosen path. For more detailed Information, Refer this link.
- filters: FileFilter[{}] (Optional) It is an Array of Objects. It defines an array of file types that can be displayed when we want to limit the user to a specific type. We can define multiple file types object belonging to different categories. The FileFilter object takes in the following parameters,
- name: String The name of the category of extensions.
- extensions: String[] The extensions array should consist of extensions without wildcards or dots as demonstrated in the code. To show all files, use the * wildcard (no other wildcard is supported). For more detailed Information, Refer this link.
In the above code, we want to restrict the user to Text files only. Hence we have defined the name as Text Files and the extensions array as [‘txt’, ‘docx’].
- properties: String[] (Optional) Contains a list of features which are available for the native dialog. It take in the following values,
- showHiddenFiles: Show Hidden files in dialog.
- createDirectory: This value is supported in macOS only. It allows creating new directories from within the dialog. In Windows, the context-menu is pre-available in the dialog (right-click in dialog window) and we can create new files and directories from it.
- treatPackageAsDirectory: This value is supported in macOS only. It treats packages such as .app folders, as a directory instead of a file.
- dontAddToRecent: This value is supported in Windows only. This value signifies that the file being saved should not be added to the recent documents list.
- showOverwriteConfirmation: This value is supported in Linux only. This value defines whether the user should be presented with a confirmation dialog if the user types in a file name that already exists in that directory.
The dialog.showSaveDialog(browserWindow, options) returns a Promise. It resolves to an Object containing the following parameters,
- canceled: Boolean Whether or not the dialog operation was cancelled.
- filePath: String (Optional) The file path chosen by the user. If the dialog operation is cancelled, it is going to be undefined.
- bookmark: String (Optional) This return String is supported in macOS only. It is a Base64 encoded String which contains the security scoped bookmark data for the saved file. This is returned when the securityScopedBookmarks parameter is defined as true in the options Object. For the different return values, Refer this link.
The fs.writeFile(file, data, options, callback) method is used to write the specified data to a file. By default, this will replace the file if it exists at the defined file path. We have specified the absolute file path, the String data to be written and a callback for handling Error in the above code. We can use the options parameter to modify the functionality. For more detailed Information, Refer this link.
We should now be able to successfully choose the file path from the dialog window, give a new file name (if required, in our code will be sample.txt by default) and save the newly created sample.txt file at that path.
Output: