Skip to content

Commit

Permalink
Simplifying exportWorkbook()
Browse files Browse the repository at this point in the history
saveBlob() replaces complexity of processAndSave()
  • Loading branch information
ddooley committed Feb 4, 2025
1 parent 1dd4766 commit ea47cdc
Showing 1 changed file with 88 additions and 87 deletions.
175 changes: 88 additions & 87 deletions lib/utils/files.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { utils as XlsxUtils, writeFile } from 'xlsx/xlsx.js';
import { utils as XlsxUtils, writeFile} from 'xlsx/xlsx.js';
import { saveAs } from 'file-saver';

/**
Expand Down Expand Up @@ -120,6 +120,7 @@ export function createWorkbookFromJSON(jsonData) {
return workbook;
}

/*
function writeWorkbook(workbook, baseName, ext, opt = {}) {
if (ext === 'xlsx' || ext === 'xls') {
// can support multiple sheets in one file
Expand All @@ -144,6 +145,7 @@ function writeWorkbook(workbook, baseName, ext, opt = {}) {
});
}
}
*/

/**
* Download matrix to file.
Expand All @@ -158,111 +160,110 @@ function writeWorkbook(workbook, baseName, ext, opt = {}) {
* @param {String} ext Extension of downloaded file.
*/
export function exportWorkbook(workbook, baseName, ext) {
switch (ext) {
case 'xlsx':
case 'xls':
writeWorkbook(workbook, baseName, ext);
break;
case 'csv':
processAndSave(
workbook,
baseName,
'csv',
',',
'text/plain;charset=UTF-8'
);
break;

case 'csv (UTF-16)':
processAndSave(
workbook,
baseName,
'csv',
',',
'text/plain;charset=UTF-16LE'
);
break;

case 'tsv':
processAndSave(
workbook,
baseName,
'tsv',
'\t',
'text/plain;charset=UTF-8'
);
break;

case 'tsv (UTF-16)':
processAndSave(
workbook,
baseName,
'tsv',
'\t',
'text/plain;charset=UTF-16LE'
);
break;

case 'csv (UTF-8, no BOM)':
processAndSave(
workbook,
baseName,
'csv',
',',
'text/plain;charset=UTF-8',
false
);
break;

case 'csv (ASCII)':
processAndSave(
workbook,
baseName,
'csv',
',',
'text/plain;charset=us-ascii',
false
);
break;
}
}

function processAndSave(
workbook,
baseName,
ext,
delimiter,
mimeType,
includeBOM = true
) {
// Often just one sheet, but if multiple, then each gets file name + _ + template (class) name
const sheets = workbook.SheetNames;
sheets.forEach((sheetName) => {
let data = '';

const worksheet = workbook.Sheets[sheetName];
const fileName = `${baseName}${sheets.length > 1 ? `_${sheetName}` : ''}.${ext.split(' ')[0]}`;
var data = '';

switch (ext) {
case 'xlsx':
case 'xls':
// Note, mimeType always set to application/zip in these cases.
writeFile(workbook, `${baseName}.${ext}`); //, opt
break;

/* Notes:
See
- https://docs.sheetjs.com/docs/api/write-options/
- https://docs.sheetjs.com/docs/api/utilities/csv#csv-output
* We want more accurate mimeTypes, so saveBlob() allows this.
* writeFile(bookType: 'csv'...) output includes the UTF-8 byte order
* mark ("BOM").
* writeFile(bookType: 'tsv'...) output will NOT include the BOM ???
* sheet_to_csv() will return JavaScript strings without the UTF-8 BOM.
* sheet_to_txt(): If encoding support is available, the output will be
* encoded in CP1200 and the UTF-16 BOM will be added. If encoding
* support is not available, the output will be encoded as a standard
* string.
* So is encoding support available?
*/
case 'csv':
// writeFile(workbook, fileName, {bookType: 'csv', FS: ','});
data = XlsxUtils.sheet_to_csv(worksheet, {FS: ','});
data = '\uFEFF' + data; //BOM
saveBlob(data, fileName, 'text/plain;charset=UTF-8');
break;

case 'csv (UTF-16)':
//writeFile(workbook, fileName, {bookType: 'txt', FS: ','});
data = XlsxUtils.sheet_to_csv(worksheet, {FS: ','});
data = '\uFEFF' + data; //BOM
saveBlob(data, fileName, 'text/plain;charset=UTF-16LE');
break;

case 'tsv': // BOM version
//writeFile(workbook, fileName, {bookType: 'csv', FS: '\t'});
data = XlsxUtils.sheet_to_csv(worksheet, {FS: '\t'});
data = '\uFEFF' + data; //BOM
saveBlob(data, fileName, 'text/plain;charset=UTF-8');
break;

case 'tsv (UTF-16)':
data = XlsxUtils.sheet_to_txt(worksheet, {FS: '\t'});
saveBlob(data, fileName, 'text/plain;charset=UTF-16LE');
break;

case 'csv (UTF-8, no BOM)':
data = XlsxUtils.sheet_to_csv(worksheet, {FS: ','});
saveBlob(data, fileName, 'text/plain;charset=UTF-8');
break;

case 'csv (ASCII)': // no BOM
data = XlsxUtils.sheet_to_csv(worksheet, {FS: ','});
saveBlob(data, fileName, 'text/plain;charset=us-ascii');
break;
}
})
};

// Saves workbook which may have multiple sheets into one or more files.
// ext: csv, csv (UTF-16), tsv, tsv (UTF-16)
// ext no BOM: csv (UTF-8, no BOM), csv (ASCII)
// This script can enhance file type with mimeType - but is that something sheetJS can't do?
function saveBlob(
data,
fileName,
mimeType
) {

/*
const sheetData = XlsxUtils.sheet_to_json(worksheet, { header: 1 });
const formattedData = sheetData
.map((row) => row.join(delimiter))
.join('\n');
data += formattedData + '\n';
// Insert BOM character.
if (includeBOM && mimeType.includes('UTF-8')) {
data = '\uFEFF' + data;
}
*/

// https://docs.sheetjs.com/docs/api/utilities/csv#csv-output
// "If encoding support is available, the output will be encoded in CP1200 and the UTF-16 BOM will be added. If encoding support is not available, the output will be encoded as a standard string.""

// Enhancing with mimeType
const blob = new Blob([data], { type: mimeType });
saveAs(
blob,
`${baseName}${sheets.length > 1 ? `_${sheetName}` : ''}.${
ext.split(' ')[0]
}`
);
});
}
saveAs(blob, fileName);
};

// TODO: refactor to export matrix
export function exportFile(matrix, baseName, ext) {
console.log("running exportFile", matrix, baseName, ext)
const worksheet = XlsxUtils.aoa_to_sheet(matrix);
const workbook = XlsxUtils.book_new();
XlsxUtils.book_append_sheet(workbook, worksheet, DEFAULT_SHEETNAME);
Expand Down

0 comments on commit ea47cdc

Please sign in to comment.