CSV for MS Excel export

This commit is contained in:
SPRINX0\prochazka
2025-11-12 10:59:50 +01:00
parent 748381fef3
commit 0ad7c0546b
2 changed files with 122 additions and 7 deletions

View File

@@ -6,6 +6,54 @@ const { getLogger } = global.DBGATE_PACKAGES['dbgate-tools'];
const logger = getLogger('csvWriter');
class RecodeTransform extends stream.Transform {
constructor(toEncoding = 'utf8') {
super({ readableObjectMode: false, writableObjectMode: false });
this.to = String(toEncoding).toLowerCase();
this.decoder = new (global.TextDecoder || require('util').TextDecoder)('utf-8', { fatal: false });
}
_encodeString(str) {
if (this.to === 'utf8' || this.to === 'utf-8') {
return Buffer.from(str, 'utf8');
}
if (this.to === 'utf16le' || this.to === 'ucs2' || this.to === 'utf-16le') {
return Buffer.from(str, 'utf16le');
}
if (this.to === 'utf16be' || this.to === 'utf-16be') {
const le = Buffer.from(str, 'utf16le');
for (let i = 0; i + 1 < le.length; i += 2) {
const a = le[i];
le[i] = le[i + 1];
le[i + 1] = a;
}
return le;
}
throw new Error(`Unsupported target encoding: ${this.to}`);
}
_transform(chunk, enc, cb) {
try {
if (!Buffer.isBuffer(chunk)) chunk = Buffer.from(chunk, enc);
const part = this.decoder.decode(chunk, { stream: true });
if (part.length) this.push(this._encodeString(part));
cb();
} catch (e) {
cb(e);
}
}
_flush(cb) {
try {
const rest = this.decoder.decode();
if (rest.length) this.push(this._encodeString(rest));
cb();
} catch (e) {
cb(e);
}
}
}
class CsvPrepareStream extends stream.Transform {
constructor({ header }) {
super({ objectMode: true });
@@ -26,18 +74,59 @@ class CsvPrepareStream extends stream.Transform {
}
}
async function writer({ fileName, encoding = 'utf-8', header = true, delimiter, quoted }) {
async function writer({
fileName,
encoding = 'utf-8',
header = true,
delimiter,
quoted,
writeBom,
writeSepHeader,
recordDelimiter,
}) {
logger.info(`DBGM-00133 Writing file ${fileName}`);
const csvPrepare = new CsvPrepareStream({ header });
const csvStream = csv.stringify({ delimiter, quoted });
const csvStream = csv.stringify({ delimiter, quoted, record_delimiter: recordDelimiter });
const fileStream = fs.createWriteStream(fileName, encoding);
// csvPrepare.pipe(csvStream);
// csvStream.pipe(fileStream);
// csvPrepare['finisher'] = fileStream;
if (writeBom) {
switch (encoding.toLowerCase()) {
case 'utf-8':
case 'utf8':
fileStream.write(Buffer.from([0xef, 0xbb, 0xbf]));
break;
case 'utf-16':
case 'utf16':
fileStream.write(Buffer.from([0xff, 0xfe]));
break;
case 'utf-16le':
case 'utf16le':
fileStream.write(Buffer.from([0xff, 0xfe]));
break;
case 'utf-16be':
case 'utf16be':
fileStream.write(Buffer.from([0xfe, 0xff]));
break;
case 'utf-32le':
case 'utf32le':
fileStream.write(Buffer.from([0xff, 0xfe, 0x00, 0x00]));
break;
case 'utf-32be':
case 'utf32be':
fileStream.write(Buffer.from([0x00, 0x00, 0xfe, 0xff]));
break;
default:
break;
}
}
if (writeSepHeader) {
fileStream.write(`sep=${delimiter}${recordDelimiter || '\n'}`);
}
csvPrepare.requireFixedStructure = true;
return [csvPrepare, csvStream, fileStream];
// return csvPrepare;
return encoding.toLowerCase() === 'utf8' || encoding.toLowerCase() === 'utf-8'
? [csvPrepare, csvStream, fileStream]
: [csvPrepare, csvStream, new RecodeTransform(encoding), fileStream];
}
module.exports = writer;

View File

@@ -68,5 +68,31 @@ export default {
},
}),
},
{
label: 'CSV file for MS Excel',
extension: 'csv',
createWriter: (fileName) => ({
functionName: 'writer@dbgate-plugin-csv',
props: {
fileName,
delimiter: ';',
recordDelimiter: '\r\n',
encoding: 'utf16le',
writeSepHeader: true,
writeBom: true,
},
}),
},
{
label: 'TSV file (tab separated)',
extension: 'tsv',
createWriter: (fileName) => ({
functionName: 'writer@dbgate-plugin-csv',
props: {
fileName,
delimiter: '\t',
},
}),
},
],
};