docs.sheetjs.com/docz/docs/03-demos/19-desktop/01-electron.md
2024-03-22 00:45:40 -04:00

9.8 KiB

title pagination_prev pagination_next sidebar_position sidebar_custom_props
Electron demos/mobile/index demos/cli/index 1
summary
Embedded NodeJS + Chromium

import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';

The NodeJS Module can be imported from the main or the renderer thread.

The "Complete Example" creates an app that looks like the screenshots below:

Windows macOS Linux

Windows screenshot

macOS screenshot

Linux screenshot

Integration Details

Electron presents a fs module. The require('xlsx') call loads the CommonJS module, so XLSX.readFile and XLSX.writeFile work in the renderer thread.

Reading Files

Electron offers 3 different ways to read files, two of which use Web APIs.

File Input Element

File input elements automatically map to standard Web APIs.

For example, assuming a file input element on the page:

<input type="file" name="xlfile" id="xlf" />

The event handler would process the event as if it were a web event:

async function handleFile(e) {
  const file = e.target.files[0];
  const data = await file.arrayBuffer();
  /* data is an ArrayBuffer */
  const workbook = XLSX.read(data);

  /* DO SOMETHING WITH workbook HERE */
}
document.getElementById("xlf").addEventListener("change", handleFile, false);

Drag and Drop

The drag and drop snippet applies to DIV elements on the page.

For example, assuming a DIV on the page:

<div id="drop">Drop a spreadsheet file here to see sheet data</div>

The event handler would process the event as if it were a web event:

async function handleDrop(e) {
  e.stopPropagation();
  e.preventDefault();

  const file = e.dataTransfer.files[0];
  const data = await file.arrayBuffer();
  /* data is an ArrayBuffer */
  const workbook = XLSX.read(data);

  /* DO SOMETHING WITH workbook HERE */
}
document.getElementById("drop").addEventListener("drop", handleDrop, false);

Electron API

XLSX.readFile reads workbooks from the file system. showOpenDialog shows a Save As dialog and returns the selected file name. Unlike the Web APIs, the showOpenDialog flow can be initiated by app code:

/* from the renderer thread */
const electron = require('@electron/remote');

/* this function will show the open dialog and try to parse the workbook */
async function importFile() {
  /* show Save As dialog */
  const result = await electron.dialog.showOpenDialog({
    title: 'Select a file',
    filters: [{
      name: "Spreadsheets",
      extensions: ["xlsx", "xls", "xlsb", /* ... other formats ... */]
    }]
  });
  /* result.filePaths is an array of selected files */
  if(result.filePaths.length == 0) throw new Error("No file was selected!");
  // highlight-next-line
  return XLSX.readFile(result.filePaths[0]);
}

:::note pass

showOpenDialog originally returned an array of paths:

var dialog = require('electron').remote.dialog;

function importFile(workbook) {
  var result = dialog.showOpenDialog({ properties: ['openFile'] });
  return XLSX.readFile(result[0]);
}

This method was renamed to showOpenDialogSync in Electron 6.

:::

Writing Files

XLSX.writeFile writes workbooks to the file system. showSaveDialog shows a Save As dialog and returns the selected file name:

/* from the renderer thread */
const electron = require('@electron/remote');

/* this function will show the save dialog and try to write the workbook */
async function exportFile(workbook) {
  /* show Save As dialog */
  const result = await electron.dialog.showSaveDialog({
    title: 'Save file as',
    filters: [{
      name: "Spreadsheets",
      extensions: ["xlsx", "xls", "xlsb", /* ... other formats ... */]
    }]
  });
  /* write file */
  // highlight-next-line
  XLSX.writeFile(workbook, result.filePath);
}

:::note pass

showSaveDialog originally returned the selected path:

var dialog = require('electron').remote.dialog;

function exportFile(workbook) {
  var result = dialog.showSaveDialog();
  XLSX.writeFile(workbook, result);
}

This method was renamed to showSaveDialogSync in Electron 6.

:::

Complete Example

:::note Tested Deployments

This demo was tested in the following environments:

OS and Version Architecture Electron Date
macOS 14.4 darwin-x64 29.1.4 2024-03-15
macOS 14.1.2 darwin-arm 27.1.3 2023-12-01
Windows 10 win10-x64 28.2.0 2024-03-04
Windows 11 win11-arm 27.1.3 2023-12-01
Linux (HoloOS) linux-x64 29.1.4 2024-03-21
Linux (Debian) linux-arm 27.1.3 2023-12-01

:::

This demo includes a drag-and-drop box as well as a file input box, mirroring the SheetJS Data Preview Live Demo

The core data in this demo is an editable HTML table. The readers build up the table using sheet_to_html (with editable:true option) and the writers scrape the table using table_to_book.

The demo project is wired for electron-forge to build the standalone binary.

  1. Download the demo files:

:::caution pass

Right-click each link and select "Save Link As...". Left-clicking a link will try to load the page in your browser. The goal is to save the file contents.

:::

These instructions can be run in a Terminal (bash) or Command Prompt window:

mkdir sheetjs-electron
cd sheetjs-electron
curl -LO https://docs.sheetjs.com/electron/package.json
curl -LO https://docs.sheetjs.com/electron/main.js
curl -LO https://docs.sheetjs.com/electron/index.html
curl -LO https://docs.sheetjs.com/electron/index.js
  1. Install dependencies:
npm install
  1. To verify the app works, run in the test environment:
npx -y electron .

The app will show.

  1. To build a standalone app, run the builder:
npm run make

This will create a package in the out\make folder and a standalone binary.

:::caution pass

On Linux, the packaging step may require additional dependencies1

:::

:::info pass

When the demo was last tested on Windows ARM, the generated binary targeted x64. The program will run on ARM64 Windows.

:::

Testing

  1. Download the test file pres.numbers

  2. Launch the generated application:

Architecture Command
darwin-x64 open ./out/sheetjs-electron-darwin-x64/sheetjs-electron.app
win10-x64 .\out\sheetjs-electron-win32-x64\sheetjs-electron.exe
linux-x64 ./out/sheetjs-electron-linux-x64/sheetjs-electron

Electron API

  1. Click "Click here to select a file from your computer". With the file picker, navigate to the Downloads folder and select pres.numbers.

The application should show data in a table.

  1. Click "Export Data!" and click "Save" in the popup. By default, it will try to write to Untitled.xls in the Downloads folder.

:::note pass

In some tests, the dialog did not have a default name.

If there is no default name, enter Untitled.xls and click "Save".

:::

The app will show a popup once the data is exported. Open the file in a spreadsheet editor and compare the data to the table shown in the application.

Drag and Drop

  1. Close the application, end the terminal process and re-launch (see step 6)

  2. Open the Downloads folder in a file explorer or finder window.

  3. Click and drag the pres.numbers file from the Downloads folder to the bordered "Drop a spreadsheet file" box. The file data should be displayed.

File Input Element

  1. Close the application, end the terminal process and re-launch (see step 6)

  2. Click "Choose File". With the file picker, navigate to the Downloads folder and select pres.numbers.

Electron Breaking Changes

The first version of this demo used Electron 1.7.5. The current demo includes the required changes for Electron 28.2.0.

There are no Electron-specific workarounds in the library, but Electron broke backwards compatibility multiple times. A summary of changes is noted below.

:::caution pass

Electron 6 changed the return types of dialog API methods. The old dialog methods have been renamed:

Electron 1 - 5 Electron 6
showOpenDialog showOpenDialogSync
showSaveDialog showSaveDialogSync
This change was not properly documented!

Electron 9 and later require the preference nodeIntegration: true in order to require('xlsx') in the renderer process.

Electron 12 and later also require worldSafeExecuteJavascript: true and contextIsolation: true.

Electron 14 and later must use @electron/remote instead of remote. An initialize call is required to enable Developer Tools in the window.

:::


  1. See "Makers" in the Electron Forge documentation. On Linux, the demo generates rpm and deb distributables. On Arch Linux and the Steam Deck, sudo pacman -Syu rpm-tools dpkg fakeroot installed required packages. ↩︎