preface

The company has a project to make web terminal and desktop terminal respectively (Electron implementation), download files in the Web terminal, directly jump to the new page, use the browser’s own downloader can obtain convenient download experience. However, the desktop cannot directly jump to a new page for download and cannot display the download progress, which affects the user experience. Therefore, the leader gave me the following requirements:

Realize a download progress bar, you can see the real-time download progress, download before you can choose the location of the file to save

Doing so makes up for some of the inherent shortcomings of Web browsers:

  • Native system tray support
  • Enhanced awareness of network status changes

Moreover, it is theoretically safer to download and carry sensitive information in the APP rather than through the system browser

I’ve been in contact with Electron before, but I don’t know much about it. The realization of this demand is realized after I look up a lot of information, so WRITE an article to record the whole process.

Page style

As Element UI framework is used in the whole project, the progress bar naturally adopts the advanced component of the frame. You only need to control the display of dialog popup and the percentage of progress. The interface style is as follows:

Choose the download location

Before that, let’s briefly introduce the modules needed for the implementation of this function, introduced in the main process and render process:

Main process module:

  • ipcMain-> Asynchronous communication from main process to renderer process.
  • dialog-> Displays native system dialogs for opening and saving files, alerts, and so on.
  • shell-> Manage files and urls using the default application.

Render process module:

  • ipcRenderer-> Asynchronous communication from the renderer process to the main process.

After users click download, they need to trigger the system popup first, instead of the custom progress popup. You need to be clear about the sequence, so you need to write a Download event in the main process first.

// Listen for the download event issued by the renderer
ipcMain.on('download'.async (evt, args) => {
// Open the system popup window to select the file download location
    dialog.showOpenDialog({
      properties: ['openFile'.'openDirectory']},(files) = > {
      saveUrl = files[0];  // Save the file path
      if(! saveUrl)return; // If the user does not select a path, proceed no further
      let url = JSON.parse(args); 
      downloadUrl = url.downloadUrl; // Get the download link passed by the renderer
      mainWindow.webContents.downloadURL(downloadUrl); // Trigger the will-download event})});Copy the code

Since electron is implemented based on Chromium, invoking the downloadURL method of webContents is equivalent to invoking the download of the underlying implementation of Chromium. The response header information will be ignored and the will-download event will be triggered.

In the rendering process, you need to send the Download event to pass the link to the file to be downloaded when the user clicks download. The code is as follows:

 ipcRenderer.send('download'.JSON.stringify({
        downloadUrl: href
 }));
Copy the code

Monitor the download process and calculate the progress

After the will-Download event is triggered, we are in the process of listening for downloads:

1. Save the download path

SetSavePath () is used to determine whether the save dialog box is displayed by default. This method takes one parameter, the path to the file. However, be sure to include the file name in the path! Otherwise, it will not report an error if the path is incorrect and will choose to store it in the default path. If you do not set this statement, the default system save popup will pop up. No configuration is required, but the save dialog does not block the process, and when you select the directory, you may have already downloaded, so showing the download progress is meaningless.

2. Listen for the download process

Misc continuous is becoming an ongoing and misc continuous is becoming interrupted according to state. If the state is downloading in progress, upload the percentage to the rendering process in real time for page display:

mainWindow.webContents.send('updateProgressing', value);
Copy the code

3. Calculate the download progress

The updated event for item retrieves the following information:

  • Item.getfilename () Specifies the name of the downloaded file
  • Item.getsavepath () The path to download the file
  • Item.getreceivedbytes () Number of bytes downloaded from the file
  • Item.gettotalbytes () Total number of bytes downloaded from the file

Knowing this, it is much easier to get the download progress:

parseInt(100 * (item.getReceivedBytes() / item.getTotalBytes()))
Copy the code

4. Listen to the download

Use the done event of item to listen for the download completion.

  • Completed -> Download completed
  • Cancelled -> Users actively cancel the download

If the download is complete, use the electron shell module to open the file (openPath) and open the file location (showItemInFolder).

 shell.showItemInFolder(filePath)
Copy the code

The completion code is as follows:

Main process:

  mainWindow.webContents.session.on('will-download'.(e, item) = > {
    const filePath = path.join(saveUrl, item.getFilename());
    item.setSavePath(filePath); // 'C:\Users\ Kim \Downloads\ 12th.zip '
    // Listen to the download process, calculate and set the progress bar progress
    item.on('updated'.(evt, state) = > {
      if ('progressing' === state) {
        // The ratio between the number of bytes received and the total number of bytes is the progress percentage
        if (item.getReceivedBytes() && item.getTotalBytes()) {
          value = parseInt(
            100 * (
              item.getReceivedBytes() / item.getTotalBytes()
            )
          )
        }
        // Send the percentages to the renderer for display
        mainWindow.webContents.send('updateProgressing', value);
        // MAC program dock, Windows taskbar display progressmainWindow.setProgressBar(value); }});// Listen for the download end event
    item.on('done'.(e, state) = > {
      // Remove the progress bar if the window is still there
      if(! mainWindow.isDestroyed()) { mainWindow.setProgressBar(-1);
      }
      // The download was cancelled or interrupted
      if (state === 'interrupted') {
        electron.dialog.showErrorBox('Download failed'.` file${item.getFilename()}The download was interrupted for some reason);
      }
      // Open the file folder after the download is successful
      if (state === 'completed') {
        setTimeout(() = > {
          shell.showItemInFolder(filePath)
        }, 1000); }}); });Copy the code

In the rendering process, lifecycle creation needs to be accepted:

 ipcRenderer.removeAllListeners('updateProgressing');
    ipcRenderer.on('updateProgressing'.(e, value) = > {
      this.$nextTick(() = > {
        this.downloadStatus = true; // Open the progress popup
        this.downloadPercent = value; // Set the download percentage
      });
    })
Copy the code

conclusion

Electron can do much more than that, and still needs to be explored. In daily work, it can be integrated with the original capability of Electron according to requirements. This function realization stepped on the pit, summed up a experience: in the implementation process, do not try to a method, in fact, another method can also achieve the needs of the want, can let oneself feel suddenly enlightened.

This is just one of the ways I’ve figured it out. If you have a better one, feel free to comment in the comments section!

Reference links:

  1. Electron builds a desktop application for downloading files
  2. Electron procedure, how to monitor the file download progress, and display the progress bar?
  3. Electron official Document