FE-P
FE-P

Reputation: 89

Exceljs : 'We found a problem with some content in "file.xlsx"(...)'

I'm having some issue with an Exceljs generated xlsx file. I've created csv files in my project for a while without any problem. But now I'm creating an xlsx file and, while I can open it clean on my Ubuntu/LibreOffice Calc, there is an error when trying to do the same on Excel (on Windows and IOS all the same).

My file's name is "export-yyyy-mm-dd.xlsx" with two sheets named "Total" and "Details", respectively.

Both sheets have a "fake" title line above columns headers. The second file has 13 columns of either string or number data for all my users while the first sheet aggregates those data in 6 number columns, with just one line under the headers.

It is that first sheet one line of data that is missing when opening the file with Excel.

What I've tried so far :

I'm out of new ideas right now so : what might be causing this problem ?

Here is a simplified version of the code that generates the first sheet :

const file = new Workbook();

function createFirstSheetHeaders(): Partial<Column>[] {
  return [
    {
      key: "usersCount",
      width: 50,
    },
    {
      key: "ratio",
      width: 40,
    },
    {
      key: "firstCount",
      width: 40,
    },
    {
      key: "total",
      width: 40,
    },
    {
      key: "secondCount",
      width: 40,
    },
    {
      key: "saved",
      width: 50,
    },
  ];
}

function firstSheetHeadersContent(): {
  usersCount: string;
  ratio: string;
  firstCount: string;
  total: string;
  secondCount: string;
  saved: string;
} {
  return {
    usersCount: "Users count",
    ratio: "Ratio",
    firstCount: "First count",
    total: "Total",
    secondCount: "Second count",
    saved: "Saved",
  };
}

  const header = "Export from 04-03-2024 for the period from 01-01-2024 to 04-07-2024";

  const firstSheet = file.addWorksheet("Total", {
    pageSetup: { orientation: "landscape" },
  });
  firstSheet.columns = createFirstSheetHeaders();
  firstSheet.mergeCells("A1:F1");
  firstSheet.getCell("A1").value = header;
  firstSheet.addRow(firstSheetHeadersContent(t));

function formatTotalDataForRow(
  usersCountInput: number,
  ratioInput: number,
  dailyTotal: number,
  firstCount: number,
  secondCount: number
): {
  usersCount: number;
  ratio: number;
  firstCount: number;
  total: number;
  secondCount: number;
  saved: number;
} {
  const parsedTransportEmissionRatio = Number(ratioInput.toFixed(4));

  const total = dailyTotal * firstCount;
  const saved = dailyTotal * secondCount;

  return {
    usersCount: usersCountInput,
    ratio: parsedTransportEmissionRatio,
    firstCount: firstCount,
    total: total,
    secondCount: secondCount,
    saved: saved,
  };

const totalData = formatTotalDataForRow(
    usersCount,
    ratioInput,
    dailyTotal,
    firstCount,
    secondCount
  );

  firstSheet.addRow(totalData);
  firstSheet.getCell("B3").numFmt = "0.00%";
}

Edit : If i only include one of the two sheets, it works just fine, whether it's the first or the second sheet. So the issue might not be related to the way this sheet is created / its content.

Due to the nature of the data and the potential huge size of it, I generate my file like this :

Here's what I do when the first sheet is filled :

  const firstSheetBuffer = await firstSheet.workbook.xlsx.writeBuffer();
  if (Buffer.isBuffer(firstSheetBuffer)) {
    passThrough.write(firstSheetBuffer);
  } else {
    const err = new Error("ExcelJs does not return a Buffer");
    passThrough.destroy(err);
    throw err;
  }
  passThrough.end();
  return file;

Upvotes: 0

Views: 661

Answers (1)

FE-P
FE-P

Reputation: 89

It turns out my problem was with s3 and not with ExcelJs. I need to commit() every row, every sheet and the whole document when any of them is finished. I also need to create my file as a WorkbookWriter (not a simple Workbook) and to pass it my stream on creation, like this :

  const file = new stream.xlsx.WorkbookWriter({
    stream: passThrough,
  });

Doing it any other way seemed to kind of corrupt the data enough for Excel to have trouble reading it, but not enough to trouble LibreOffice.

Upvotes: 1

Related Questions