Parse excel file and create JSON format in exceljs ON nodejs - node.js

I have this excel file
I need to convert the data excel from file to JSON like below
[
{
"no_pemohonan": "PNL-202109200826210023105",
"sumber_data": "HOSTS",
"tgl_permohonan": "2021-09-20",
},
{
"no_pemohonan": "PNL-202109200845131363376",
"sumber_data": "HOSTS",
"tgl_permohonan": "2021-09-20",
},
...
]
I could make the data with this comment but i have to set the object again like below syntax
const excel = require('exceljs');
const workbook = new excel.Workbook();
await workbook.xlsx.load(objDescExcel.buffer);
let json = workbook.model;
let worksheetsArr = json.worksheets.length;
const arrRow = [];
const arrIdPembatalan = [];
// looping per worksheet
for (let index = 0; index < worksheetsArr; index++) {
let worksheet = workbook.worksheets[index];
// console.log("worksheet " + worksheet);
const rowlast = worksheet.lastRow;
// looping semua row untuk
worksheet.eachRow(async (row, rowNumber) => {
let new_row = row.values
// console.log(new_row);
let no_permohonan= new_row[2]
let sumber_data = new_row[3]
let tgl_permohonan = new_row[4]
let header = {
no_permohonan: no_permohonan,
sumber_data : sumber_data ,
tgl_permohonan : tgl_permohonan ,
};
arrIdPembatalan.push(header)
}
})
}
I want to set the header automatically without have to set the header again like above syntax.
I have seen this solution but it was written in xlsx library, while i am using exceljs.

Here is a nodejs implement.
(async function() {
const excel = require('exceljs');
const workbook = new excel.Workbook();
// use readFile for testing purpose
// await workbook.xlsx.load(objDescExcel.buffer);
await workbook.xlsx.readFile(process.argv[2]);
let jsonData = [];
workbook.worksheets.forEach(function(sheet) {
// read first row as data keys
let firstRow = sheet.getRow(1);
if (!firstRow.cellCount) return;
let keys = firstRow.values;
sheet.eachRow((row, rowNumber) => {
if (rowNumber == 1) return;
let values = row.values
let obj = {};
for (let i = 1; i < keys.length; i ++) {
obj[keys[i]] = values[i];
}
jsonData.push(obj);
})
});
console.log(jsonData);
})();
test result
$ node ./test.js ~/Documents/Book1.xlsx
[
{
no_pemohonan: 'PNL-202109200826210023105',
sumber_data: 'HOSTS',
tgl_permohonan: 2021-09-20T00:00:00.000Z
},
{
no_pemohonan: 'PNL-202109200845131363376',
sumber_data: 'HOSTS',
tgl_permohonan: 2021-09-20T00:00:00.000Z
}
]

If dealing with large files, I would explore stream processing using the following libraries:
Use exceljs to read .xlsx file as stream and write to .csv as stream:
// read from a stream
const readStream = fs.createReadStream('largeWorkbook.xlsx');
const workbook = new Excel.Workbook();
await workbook.xlsx.read(readStream);
// write to stream
const writeStream = fs.createWriteStream('largeWorksheet.csv');
await workbook.csv.write(writeStream, { sheetName: 'Page name' });
Then use csvtojson to transform CSV to JSON:
import csvToJson from 'csvtojson'
const readStream = fs.createReadStream('largeWorksheet.csv')
const writeStream = fs.createWriteStream('largeWorksheet.json')
readStream
.pipe(csvToJson())
.pipe(writeStream)
This will work for large files even on hardware with low memory.
Full code snippet:
import fs from 'fs'
import Excel from 'exceljs'
import csvToJson from 'csvtojson'
const xlsxRead = fs.createReadStream('largeWorkbook.xlsx')
const csvWrite = fs.createWriteStream('largeWorksheet.csv')
const csvRead = () => fs.createReadStream('largeWorksheet.csv')
const jsonWrite = fs.createWriteStream('largeWorksheet.json')
(async function process() {
const workbook = new Excel.Workbook()
await workbook.xlsx.read(xlsxRead)
await workbook.csv.write(csvWrite, { sheetName: 'Worksheet Name' })
csvRead()
.pipe(csvToJson())
.pipe(jsonWrite)
})() // this immediately-invoked wrapper function is just for Node.js runtimes
// that don't support top-level await yet
// if running via `--esm` or from `.mjs` file, it can be ommitted

var Excel = require('exceljs');
var ReadExcelCSV = function (fileType, fileName, filePath, delimeter, textQualifier) {
var deffered = q.defer();
var workbook = new Excel.Workbook();
var finalFilePath = filePath + fileName;
if (fileType == "excel") {
console.log("File Type: Excel");
workbook.xlsx.readFile(finalFilePath).then(function () {
ParseExcelCSVFile(workbook).then(function (resp) {
deffered.resolve(resp);
}, function (error) {
logger.info("Error in Parsing Excel/CSV");
});
}, function (err) {
logger.info("Error In Read Excel: " + JSON.stringify(err));
});
} else {
if (delimeter != undefined && textQualifier != undefined) {
var options = {};
options.delimiter = delimeter;
options.quote = textQualifier;
options.dateFormats = [];
workbook.csv.readFile(finalFilePath, options).then(function () {
ParseExcelCSVFile(workbook).then(function (resp) {
// fs.unlink(finalFilePath);
deffered.resolve(resp);
}, function (error) {
logger.info("Error in Parsing Excel/CSV");
deffered.reject(error);
});
}, function (error) {
logger.info("Error In Read CSV: " + JSON.stringify(error));
deffered.reject(error);
});
} else {
workbook.csv.readFile(finalFilePath).then(function () {
ParseExcelCSVFile(workbook).then(function (resp) {
deffered.resolve(resp);
}, function (error) {
logger.info("Error in Parsing Excel/CSV");
deffered.reject(error);
});
}, function (error) {
logger.info("Error In Read CSV: " + JSON.stringify(error));
deffered.reject(error);
});
}
}
return deffered.promise;
};
var ParseExcelCSVFile = function (workbook) {
try {
var deffered = q.defer();
var objresult = [];
var objheaders = [];
var worksheet = workbook.getWorksheet(1);
worksheet.eachRow(function (row, rowNumber) {
var currentobj = {};
row.eachCell({
includeEmpty: true
}, function (cell, colNumber) {
if (rowNumber == 1) {
objheaders.push(cell.value);
} else {
currentobj[objheaders[colNumber - 1]] = cell.value == null ? '' : cell.value;
}
});
if (rowNumber != 1) {
objresult.push(currentobj);
}
});
deffered.resolve(objresult);
return deffered.promise;
} catch (ex) {
logger.error("Error in ParseExcel: " + ex.stack);
}
};
I wrote this code quite a long time ago so you will see an old module like deffered which you can change easily, but it will help in what you are trying to achieve. It can read and parse excel and csv both.

Related

how to save excel file converted to base64 in directory with xlsx?

I am trying to save an excel file that I get from a post as base64, this conversion is done in my view, once I convert it, I try to save it with the xlsx library, this file is saving fine but when opening the file, it does not contain nothing. Can someone help me, in knowing what I am doing wrong?
my following code is:
private async getCurp(req: Request, res: Response) {
var datos = [];
let arrayusers = {};
const {
curp
} = req.body;
const newCurp = new CurpModel({
curp
});
const path = "C:\\Miroute"
var bufferFile = Buffer.from(curp, "base64");
const data = XLSX.read(bufferFile, { type: 'buffer' })
XLSX.writeFile(data, "excel.xls");
try {
return Ok<CurpType>(res, newCurp);
}
catch (error) {
console.log(error);
return ServerError(res);
}
In my component I convert my excel file to base64 in this way, iam using react
handleFileChange = e => {
let idCardBase64 = '';
this.getBase64(e.target.files[0], (result) => {
idCardBase64 = result;
console.log(idCardBase64)
this.setState({
file: idCardBase64,
})
});
}
getBase64(file, cb) {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
cb(reader.result)
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
Please can someone help me?
My solution: I had not realized that my input parameter had a different name so it was undefined, the code looked like this:
private async getCurp(req: Request, res:Response){
var datos = [];
let arrayusers = {};
const {
file
} = req.body;
const newCurp = new CurpModel({
file
});
const bufferExcel = Buffer.from(newCurp.file.toString().replace("data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64", ""),'base64');
const workbook = XLSX.read(bufferExcel, { type: 'buffer' });
const sheetNamesList = workbook.SheetNames;
// parse excel data to json
const excelData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetNamesList[0]]);
console.log(excelData);
try{
return Ok<CurpType>(res, newCurp);
}
catch(error){
console.log(error);
return ServerError(res);
}
}

Synchronous NodeJS batch job

I'm trying to write a batch script that will
Read XMLs from a directory
Parse each XML and find a value to use for DB(database) Lookup
Use the parsed value to DB lookup additional metadata
Populate XML with the metadata retrieved from DB lookup (step 4)
Write updated XML to complete directory
Close DB connection
The issue I'm running into is that I cannot control the code execution order so that I can close the DB connection at the end of the script. If I attempt to close the connection, I get a 'connection undefined' error. Below is my code for reference. Is there a good way to accomplish something like this in NodeJs, or should I look at doing this in Java or some other language?
enter code here
'use strict';
let fs = require('fs');
let xml2js = require('xml2js');
const oracledb = require('oracledb');
const dbConfig = require('./dbconfig.js');
function pad(number, length)
{
var str = '' + number;
while (str.length < length)
{
str = '0' + str;
}
return str;
}
async function run() {
try {
// Get a non-pooled connection
let connection;
if (!connection)
{
connection = await oracledb.getConnection(dbConfig);
console.log('Connection was successful!');
}
let directory = "EDI_XMLS";
let dirBuf = Buffer.from(directory);
//var f = 0;
let files = fs.readdirSync(directory);
console.log(files);
for (let f = 0; f < files.length; f++)
{
let parser = new xml2js.Parser();
var xml_json_data = "";
// read the file
await fs.readFile(directory + "/" + files[f], async function(err, data) {
// parse the file
await parser.parseString(data, async function(err, result) {
let results;
var line_count = result.page.GLLines[0].GLLine.length;
console.dir('Invoice: ' + result.page.InvoiceNumber[0]);
console.dir('Line Total: ' + line_count);
console.log('File: ' + f);
try
{ // Lookup Data
results = await connection.execute('SELECT BUSINESS_UNIT, OPERATING_UNIT, DEPTID,PRODUCT,a.effdt FROM SYSADM.PS_A_NSS_SHPTO_ACC#FDEV a where(a.a_ship_to_customer = :shipTo) order by a.effdt
desc',[pad(result.page.VoucherDescription[0], 10)], {
maxRows: 2
});
console.log(results.metaData);
console.log(results.rows);
}
catch (err)
{
console.error(err);
}
for (let i = 0; i < line_count; i++) // Populate data
{
result.page.GLLines[0].GLLine[i].GLBU[0] = results.rows[0][0];
result.page.GLLines[0].GLLine[i].OpUnit[0] = results.rows[0[1];
result.page.GLLines[0].GLLine[i].Department[0] = results.rows[0][2];
result.page.GLLines[0].GLLine[i].Product[0] = results.rows[0][3];
}
// Write to File
var builder = new xml2js.Builder();
var xml = builder.buildObject(result);
await fs.writeFile("complete/" + files[f], xml, function(err, data) {
if (err) console.log(err);
console.log("successfully written our update xml to file");
console.dir('BUs: ' + JSON.stringify(result.page));
}); //end write
}); //end parser
}); //end readfile
console.log('End');
} // async for
}
catch (err)
{
console.error(err);
}
finally
{
await connection.close();
console.log('Finally Done');
}
}
run();
console.log('completely Done');

Combine PDF files in a loop ( Hummus-Recipe )

I work with Hummus-Recipe library and it's work fine but I want to make a function that accept array of files to append all files to one.
This is my code that work:
const filesRoot = './uploads';
router.route('/')
.get( async (request, response) => {
const src = filesRoot + '/one.pdf';
const appendedFile = filesRoot + '/two.pdf';
const appendedFile2 = filesRoot + '/three.pdf';
const output = filesRoot + '/new.pdf';
const recipe = new HummusRecipe(src, output);
recipe
.appendPage(appendedFile)
.appendPage(appendedFile2)
.endPDF();
});
How can I take this code and make it accept array??
Something like that:
let combinePdfFiles = (array) => {
for (let i = 0; i < array.length; i++) {
}
};
thanks.
You can use easy-pdf-merge package that let you merge an array of some pdf files.
Usage:
const merge = require('easy-pdf-merge');
merge(source_files,dest_file_path,function(err){
if(err) {
return console.log(err)
}
console.log('Success')
});
Example:
merge(['File One.pdf', 'File Two.pdf'], 'File Ouput.pdf', function(err){
if(err) {
return console.log(err)
}
console.log('Successfully merged!')
});
I create this function and it work.
const combinePdfFiles = async (files, companyID, flowID) => {
const filesRoot = `./uploads/${companyID}/${flowID}`;
try {
const originalFile = `${filesRoot}/${files[0]}`;
const output = `${filesRoot}/combined.pdf`;
const recipe = new HummusRecipe(originalFile, output);
for (let i = 1; i < files.length; i++) {
recipe
.appendPage(`${filesRoot}/${files[i]}`);
}
recipe.endPDF();
} catch (error) {
throw error;
}
};

Why doesn't my async function return any result?

I wrote this small program to fetch data. This however is done async. Since I nonetheless need to use the function holeVertreter(kzl) as a function in another module, I'd like to get a return value which I can eventually pass on.
Excuse my spaghetti code (I usually prettify the code when I am done with my task ...).
Credentials are stored in a file and are therefore not found in this file.
I'd like to end up with "vertreter" as a return value.
Thank you in advance.
const node = require("deasync");
const DSB = require('dsbapi');
const tabletojson = require('tabletojson');
const https = require('https');
const cred = require("./vertrCred");
const dsb = new DSB(cred["dsb"]["user"], cred["dsb"]["passw"]); //Sanitized - no Credentials here
//Stackoverflow 2332811
String.prototype.capitalize = function(lower) {
return (lower ? this.toLowerCase() : this).replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); });
};
function holePlan(kuerzel) {
dsb.fetch()
.then(data => {
const timetables = DSB.findMethodInData('timetable', data);
const tiles = DSB.findMethodInData('tiles', data);
var tilesStr = JSON.stringify(tiles["data"][0]["url"]);
var url = JSON.parse(tilesStr);
https.get(url, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end',() => {
var tableasjson = tabletojson.convert(data);
var erstetab = tableasjson[0];
var zweitetab = tableasjson[1];
var drittetab = tableasjson[2];
var viertetab = tableasjson[3];
var fuenftetab = tableasjson[4];
var sechstetab = tableasjson[5];
var siebtetab = tableasjson[6];
var achtetab = tableasjson[7];
if (typeof kuerzel === "undefined")
{
var regenechse = '(Aaa|Aaa[A-Za-z?]|[A-Za-z?]Aaa)';
}
else {
var name = kuerzel.capitalize(true);
var regenechse = '('+name+'|'+name+'[A-Za-z?]|[A-Za-z?]'+name+')';
}
const regex = new RegExp(regenechse,'g');
var sammel = Object.assign(drittetab,fuenftetab);
var z= 0;
var vertreter = {}
var y = JSON.parse(JSON.stringify(sammel));
for (i=0;i<y.length;i++) {
if (typeof y[i].Vertreter =='undefined') {
}
else {
if(y[i].Vertreter.match(regex))
{
z += 1;
vertreter[z] = y[i];
}
}
}
if (z == 0) {
// console.log("Es gibt nichts zu vertreten");
}
else {
//console.log("Es werden "+z+" Stunden vertreten");
return (vertreter);
} ;
});
})
})
.catch(e => {
// An error occurred :(
console.log(e);
});
}
//Stackoverflow
function warte(promise) {
var done = 0;
var result = null;
promise.then(
function (value) {
done = 1;
result = value;
return (value);
},
function (reason) {
done = 1;
throw reason;
}
);
while (!done)
node.runLoopOnce();
return (result);
}
function holeVertretung(kzl) {
var aufgabe = new Promise((resolve,reject) => {
setTimeout(resolve,1000,holePlan(kzl));
});
var ergebnis = warte(aufgabe);
if (typeof ergebnis === "undefined") {
console.log("Mist");
}
else {
console.log(ergebnis);
}
return ergebnis;
}
holeVertretung("Aaa");
That's not the right way to work with promises. If you do such infinite loop, it beats the whole purpose of using promises. Instead, return value from the promise, and use async-await like this:
function warte(promise) {
var done = 0;
var result = null;
return promise.then(
...
}
async function holeVertretung(kzl) {
var aufgabe = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, holePlan(kzl));
});
var ergebnis = await warte(aufgabe);
...
If async-await does not work for some reason, use then clause:
warte(aufgabe).then(value => {
var ergebnis = value;
});

how to parse data from two csv in node.js

I am very new to node.js. I have one script that will parse the csv and generate the required output file. Now I want to fetch some of column data from another csv at the same time and add that value to the output file.
Script :
var csv = require('csv');
var fs = require('fs');
var progress = require('progress-stream');
var date = require('date-and-time');
var indexStat = 0;
var header = [];
var headerLine = '$lang=en\n\nINSERT_UPDATE Customer;uid;name;address;phoneno'
var delimeter = ',';
var semicolon = ';';
var inputFile = __dirname+'/project/customer.csv';
var outputFile = __dirname+'/project/customer.impex';
var inputFileName = 'customer.csv';
var outputFileName = 'customer.impex';
function generateRecord(json) {
var record = semicolon + json.uid + semicolon + json.name + semicolon + json.address;
return record;
}
var writeStream = fs.createWriteStream(outputFile);
var parser = csv.parse({
delimiter: delimeter
}, function (err, data) {
if (err) {
console.log(err);
}
});
var transformer = csv.transform(function (data) {
var line = '';
if (indexStat == 0) {
header = data;
var line = headerLine;
} else {
var line = generateRecord(generateRecordObject(data));
}
indexStat++;
writeStream.write(line + '\r\n');
});
function stringSplitter(dataRow) {
var str = dataRow.toString();
return str.split(delimeter);
}
function generateRecordObject(dataRow) {
var record = {};
dataRow.forEach(function (value, index) {
if (header[index] != '') {
record[header[index].toLowerCase()] = value;
}
});
return record;
}
var stat = fs.statSync(inputFile);
var str = progress({
length: stat.size,
time: 100
});
str.on('progress', function (progress) {
writeCompletedPercentageForRead(progress.percentage, progress.eta, progress.runtime, progress.speed);
});
function removeLineBreaks(obj) {
obj = obj.replace(/\\N/g, '');
obj = obj.replace(/&/g, '&');
return obj;
}
function writeCompletedPercentageForRead(p, e, r, s) {
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write(`${inputFileName} read in progress to write ${outputFileName} ... Completed:${parseInt(p, 10)} %, ETA:${e} seconds, Elapsed:${r} seconds, Rate:${parseInt(s/1024, 10)} KBps`);
};
fs.createReadStream(inputFile).pipe(str).pipe(parser).pipe(transformer);
customer.csv ->
uid,name,address
1234,manish,bangalore
The above script is working fine and generating customer.impex file as below
INSERT_UPDATE Customer;uid;name;address;phoneno
;1234;manish;bangalore
Now i want to populate phoneno as well but phoneno field is define in another csv file lets say 'customer_phone.csv'.
customer_phone.csv -
uid,phoneno
1234,98777767467
I want to match uid column of customer.csv with customer_phone.csv and get the phoneno from that csv. Finally i want to add phoneno in the customer.impex outfile file.
I have no idea how to parse two csv here and generate the file. Any help ?
var Converter = require("csvtojson").Converter;
var fs = require("fs");
var pathToMainCSV = "./customer.csv";
var pathToContactsCSV = "./contact.csv";
var customerConverter = new Converter({});
var contactConverter = new Converter({});
var contanierObj = {};
function processContacts() {
fs.createReadStream(pathToContactsCSV).pipe(contactConverter);
}
function createImpexFile() {
var headerLine = '$lang=en\n\nINSERT_UPDATE Customer;uid;name;address;phoneno;\n';
Object.keys(contanierObj).forEach(obj => {
Object.keys(contanierObj[obj]).forEach(data => {
headerLine += contanierObj[obj][data] + ';';
});
headerLine += '\n';
});
fs.writeFile("./new.impex", headerLine, function(err) {
if (err) {
return console.log(err);
}
console.log("The file was saved!");
});
}
customerConverter.on("end_parsed", function(jsonArray) {
jsonArray.forEach(v => {
contanierObj[v.uid] = v;
});
processContacts();
});
contactConverter.on("end_parsed", function(jsonArray) {
jsonArray.forEach(v => {
contanierObj[v.uid].contact = v.phoneno;
});
createImpexFile();
});
fs.createReadStream(pathToMainCSV).pipe(customerConverter);
Kindly use something like i have done above, format the string according to your needs

Resources