File Upload in ElectronJS

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 File Upload functionality 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.

  • Project Structure:
    Project Structure

Example: We will start by building the Electron Application for File Upload functionality 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.

    npm install electron --save

    This will install the required node_modules dependencies. Install axios package using npm.

    npm install axios --save

    This package is a Promise based HTTP client for NodeJS. This package is used to make HTTP calls to REST APIs. For more detailed information on axios, Refer this link. Create the sample.txt file in the assets folder for demo purposes.

    package.json:

    {
      "name": "electron-fileupload",
      "version": "1.0.0",
      "description": "File Upload in Electron",
      "main": "main.js",
      "scripts": {
        "start": "electron ."
      },
      "keywords": [
        "electron"
      ],
      "author": "Radhesh Khanna",
      "license": "ISC",
      "dependencies": {
        "axios": "^0.19.2",
        "electron": "^8.2.5"
      }
    }
    
  • Step 2: This is the main.js file. For the Boilerplate code of the main.js file, Refer this link. We have modified the code to suit our project needs.

    main.js:

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    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.

    chevron_right

    
    

  • 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:

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    <!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>.
      
        <h3>File Upload in Electron</h3>
        <button id="upload">Upload File</button>
          
        <!-- Adding Individual Renderer Process JS File -->
        <script src="index.js"></script>
      </body>
    </html>

    chevron_right

    
    

  • 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 Upload File button does not have any functionality associated with it yet. The dialog.showOpenDialog(browserWindow, options) takes in the following parameters.
    • browserWindow: BrowserWindow (Optional) The BrowserWindow Instance. This argument allows the 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 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 Upload File button.
    • options: Object It takes in the following parameters,
      • title: String (Optional) The title to be displayed on the dialog window.
      • defaultPath: String (Optional) The directory to be opened as defined by the default path on clicking the Upload File button.
      • 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 Upload.
      • message: String (Optional) This parameter is supported in macOS only. This is used to display the custom message above input boxes.
      • securityScopedBookmarks: Boolean (Optional) This parameter is supported in macOS only. This parameter is used to create security scoped bookmarks when packaged for the Mac App Store. 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 or selected 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: [] 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 take in the following values,
        • openFile: Allows the Files to be selected.
        • openDirectory: Allows directory/folders to be selected.
        • multiSelections: Allows multiple files to be selected in the dialog.
        • 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.
        • promptToCreate: This value is supported in Windows only. This value is used when the filepath entered in the dialog does not exist on the System. At this time, it should prompt the user to create. This does not actually create the file at the path but allows non-existent paths to be returned that can further be created and used by the application.
        • noResolveAliases: This value is supported in macOS only. It disables the automatic resolution of filepath of the alias to its original target. Selected aliases will now return the alias path instead of their target path.
        • 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/directory being chosen should not be added to the recent documents list.

        Note: According to the official Electron documentation, the open dialog cannot be both a file Selector and a directory selector in Windows and Linux OS. If both the properties are specified, [‘openFile’, ‘openDirectory’] on these platforms, directory selector will be shown and we won’t be able to select files. Hence, in the code, we have specified different dialog properties for win32/linux and darwin platform.

    The dialog.showOpenDialog(browserWindow, options) returns a Promise. It resolves to an Object containing the following parameters,

    • canceled: Boolean Whether or not the dialog operation was cancelled.
    • filePaths: String[] An Array of filepaths chosen by the user. If the dialog operation is cancelled, it is going to be an empty Array. In case the multiSelections value is not provided in properties, the filePaths array will return a single element.
    • bookmarks: String[] (Optional) This String array is supported in macOS only. This is returned when the securityScopedBookmarks parameter is specified as true in the options Object.
    • index.js: Add the following snippet in that file.

      filter_none

      edit
      close

      play_arrow

      link
      brightness_4
      code

      const electron = require('electron');
      const path = require('path');
        
      // Importing dialog module using remote
      const dialog = electron.remote.dialog;
        
      var uploadFile = document.getElementById('upload');
        
      // Defining a Global file path Variable to store 
      // user-selected file
      global.filepath = undefined;
        
      uploadFile.addEventListener('click', () => {
      // If the platform is 'win32' or 'Linux'
          if (process.platform !== 'darwin') {
              // Resolves to a Promise<Object>
              dialog.showOpenDialog({
                  title: 'Select the File to be uploaded',
                  defaultPath: path.join(__dirname, '../assets/'),
                  buttonLabel: 'Upload',
                  // Restricting the user to only Text Files.
                  filters: [
                      {
                          name: 'Text Files',
                          extensions: ['txt', 'docx']
                      }, ],
                  // Specifying the File Selector Property
                  properties: ['openFile']
              }).then(file => {
                  // Stating whether dialog operation was
                  // cancelled or not.
                  console.log(file.canceled);
                  if (!file.canceled) {
                    // Updating the GLOBAL filepath variable 
                    // to user-selected file.
                    global.filepath = file.filePaths[0].toString();
                    console.log(global.filepath);
                  }  
              }).catch(err => {
                  console.log(err)
              });
          }
          else {
              // If the platform is 'darwin' (macOS)
              dialog.showOpenDialog({
                  title: 'Select the File to be uploaded',
                  defaultPath: path.join(__dirname, '../assets/'),
                  buttonLabel: 'Upload',
                  filters: [
                      {
                          name: 'Text Files',
                          extensions: ['txt', 'docx']
                      }, ],
                  // Specifying the File Selector and Directory 
                  // Selector Property In macOS
                  properties: ['openFile', 'openDirectory']
              }).then(file => {
                  console.log(file.canceled);
                  if (!file.canceled) {
                    global.filepath = file.filePaths[0].toString();
                    console.log(global.filepath);
                  }  
              }).catch(err => {
                  console.log(err)
              });
          }
      });

      chevron_right

      
      

    Output:

  • Step 5: Once we have obtained the file path from the dialog window, we can follow any of the two approaches:
    • Approach 1: Upload the file to a server by making an HTTP POST REST API call and let the server handle the file processing. We will use the axios package that was installed earlier to achieve this.
      In index.js file, add the following code just after console.log(global.filepath); } within the Promise
      index.js:

      filter_none

      edit
      close

      play_arrow

      link
      brightness_4
      code

      const fs = require('fs');
      const axios = require('axios');
        
      if (global.filepath && !file.canceled) {
               var formData = new FormData();
               formData.append('file', fs.createReadStream(global.filepath));
               axios.post('[Custom URL]', formData, {
               headers: {
                  'Content-Type': 'multipart/form-data'
                 }
              }); 
            
        
      // ...

      chevron_right

      
      

      Explanation: We have appended the file to a formData object using the fs module, and set the Content-Type header to multipart/form-data for the POST Request. Replace the [Custom URL] with a URL of the REST API to receive the file on the server side. Once we have received the file on the server side, we can let the server handle the file processing and display the respective response.

    • Approach 2: Process the file on the system itself by reading/manipulating the contents of the file. We can use the fs module to read the contents of the file, and then perform further manipulations as required.
      In index.js file, add the following code just after console.log(global.filepath); } within the Promise

      filter_none

      edit
      close

      play_arrow

      link
      brightness_4
      code

      const fs = require('fs');
      const fs = require('fs');
        
      if (global.filepath && !file.canceled) {
        fs.readFile(global.filepath, {encoding: 'utf-8'}, function(err,data) {
           if (!err) {
                console.log('received data: ' + data);
           } else {
                console.log(err);
            }
         });
       }
        
      //...

      chevron_right

      
      

      Explanation: We will read the file from the GLOBAL file path variable that was updated from the dialog window with standard UTF-8 encoding. Once we have successfully fetched the contents of the file, we can update/manipulate the contents as per the desired functionality.

      sample.txt:
      sample.txt

      Output:

full-stack-img




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


Article Tags :

Be the First to upvote.


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.