4

Context

  • NodeJS 10
  • ExcelJS 0.8.5
  • LibreOffice 5.1.6.2 on Ubuntu

Issue

I am trying to create a multi-sheet Excel file with ExcelJS. I am following the official documentation from the ExcelJS github page.

The first step is the creation of the workbook. In my case, i want a stream because i will append lot of datas.

// Create Excel Workbook Stream
const workbookStream = new Excel.stream.xlsx.WorkbookWriter({
  filename: path,
  useStyles: true,
  useSharedStrings: true,
});

Then i add sheet to the created workbook's stream as said into the documentation Worksheet Properties.

const sheet = workbookStream.addSheet('sheet1'); // Throw here

But in this way, i got the following error :

'Type error: workbookStream.addSheet is not a function


I have also found a code that do not throw but do not work and do not create many sheets.

const header = ['A', 'B', 'C'];

const sheet1 = Excel.addSheetOnWorkbook({
  workbook: workbookStream,
  name: 'sheet1',
});

const sheet2 = Excel.addSheetOnWorkbook({
  workbook: workbookStream,
  name: 'sheet2',
});

sheet.addRow(header).commit();

sheet.addRow(header).commit();

await workbookStream.commit();

In this case, only the sheet1 is created (opening with LibreOffice 5.1.6.2).

Any way to resolve this case with ExcelJS ?

user7364588
  • 1,014
  • 2
  • 12
  • 32

1 Answers1

5

I don't know how much help this is to you, but on my environment:

  • Windows 10 (10.0.17134.0)
  • Node 10.15.0
  • exceljs 1.6.0

Once I set the worksheet.state to visible, I can see it in LibreOffice:

const Excel = require('exceljs');
const workbook = new Excel.Workbook();
const fs = require('fs');
const filename = "test.xlsx";
const sheetNames = ["A", "B", "C", "D"];

sheetNames.forEach(sheetName => {
    let worksheet = workbook.addWorksheet(sheetName);
    // I believe this needs to be set to show in LibreOffice.
    worksheet.state = 'visible';
});

const stream = fs.createWriteStream(filename);
workbook.xlsx.write(stream)
.then(function() {
    console.log(`File: ${filename} saved!`);
    stream.end();
}).catch(error => {
    console.err(`File: ${filename} save failed: `, error);
});

Using the Streaming XLSX WorkbookWriter:

const Excel = require('exceljs');
const sheetNames = ["A", "B", "C", "D"];

const workbook = new Excel.stream.xlsx.WorkbookWriter( { filename: './streamed-workbook.xlsx' } );

sheetNames.forEach(sheetName => {
    let worksheet = workbook.addWorksheet(sheetName);
    worksheet.state = 'visible';
    worksheet.commit();
});

// Finished the workbook.
workbook.commit()
.then(function() {
    console.log(`Worksheet committed!`);
});

I'll test on an Ubuntu machine as well.

XSLX files are simply .zip files containing multiple .xml files, so you can inspect the XML data yourself.

To show the raw Xml (worksheet.xml) produced by LibreOffice versus exceljs:

LibreOffice

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
    <fileVersion appName="Calc"/>
    <workbookPr backupFile="false" showObjects="all" date1904="false"/>
    <workbookProtection/>
    <bookViews>
        <workbookView showHorizontalScroll="true" showVerticalScroll="true" showSheetTabs="true" xWindow="0" yWindow="0" windowWidth="16384" windowHeight="8192" tabRatio="500" firstSheet="0" activeTab="1"/>
    </bookViews>
    <sheets>
        <sheet name="A" sheetId="1" state="visible" r:id="rId2"/>
        <sheet name="B" sheetId="2" state="visible" r:id="rId3"/>
    </sheets>
    <calcPr iterateCount="100" refMode="A1" iterate="false" iterateDelta="0.001"/>
    <extLst>
        <ext xmlns:loext="http://schemas.libreoffice.org/" uri="{7626C862-2A13-11E5-B345-FEFF819CDC9F}">
            <loext:extCalcPr stringRefSyntax="CalcA1"/>
        </ext>
    </extLst>
</workbook>

exceljs

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x15" xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main">
    <fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="9303"/>
    <workbookPr defaultThemeVersion="164011" filterPrivacy="1"/>
    <sheets>
        <sheet sheetId="1" name="A" state="show" r:id="rId3"/>
        <sheet sheetId="2" name="B" state="show" r:id="rId4"/>
        <sheet sheetId="3" name="C" state="show" r:id="rId5"/>
        <sheet sheetId="4" name="D" state="show" r:id="rId6"/>
    </sheets>
    <calcPr calcId="171027"/>
</workbook>

One more thing

The sheet title can corrupt the XML file structure when some non alphanum characters are set.

Be careful about sheet title !

Terry Lennox
  • 29,471
  • 5
  • 28
  • 40
  • You are running on **Windows 10** so i suppose your are using **Microsoft Office Excel** to open the result file ? It could lead to different behavior. – user7364588 Jan 17 '19 at 09:56
  • 1
    @Alrick, I've tried with both and I'm getting an interesting result.. the Sheets show correctly in Excel but only sheet A shows in LibreOffice. When I unzip the .xlsx file, I can see in the worksheets directory that there are indeed 4 work sheets... weirdness! – Terry Lennox Jan 17 '19 at 10:06
  • 1
    Ok i'm glad you light this behavior – user7364588 Jan 17 '19 at 10:10
  • So I think the data is there.. just for some reason it's not showing in Libre Office.. probably some disagreement over how the data is formatted. Also on the very bottom left in LibreOffice it shows "Sheet 1 of 4" so it's kind of half working... – Terry Lennox Jan 17 '19 at 10:11
  • In my case, i do not see the "Sheet 1 of 4" on LibreOffice, maybe because i use the `WorkbookWriter`. In your example, your are creating the object and then write the whole object into file. By the way, the documentation use `'show'` instead of `'visible'` for `sheet.state` parameter. – user7364588 Jan 17 '19 at 10:33
  • 1
    I made the state change to visible since this is what the Xml shows when I create a new workbook with multiple sheets in LibreOffice. The workaround is making a difference on my environment in any case! – Terry Lennox Jan 17 '19 at 10:38