Nodejs streams writablestream drain event not firing - node.js

I am trying to read in a large file, do some computation and then write to a much bigger file. To prevent excessive memory consumption, I am using streams. The problem that I am facing is that the writestream is not firing the "drain" event, which signals that the writes have been flushed to disk. In order to prevent "back-pressure", I am waiting for the drain event to be fired before I start writing to the buffer again. While debugging I found that after a .write() call returns false and the line fvfileStream.once('drain', test) is executed, the program just stops and does not do anything.
Here is the code:
var fs = require('fs');
//a test function I created to see if the callback is called after drain.
var test = function(){
console.log("Done Draining");
}
fs.readFile('/another/file/to/be/read', {
encoding: "utf8"
}, function(err, data) {
if (err) throw err;
//Make an array containing tags.
var tags = data.split('\n');
//create a write stream.
var fvfileStream = fs.createWriteStream('/path/TagFeatureVectors.csv');
//read in the question posts
var qfileStream = fs.createReadStream('/Big/file/QuestionsWithTags.csv', {
encoding: "utf8"
});
var partialRow = null;
var writable = true;
var count = 0;
var doRead = function() {
var qData = qfileStream.read();
var questions = qData.split('\n');
if (partialRow != null) {
questions[0] = partialRow + questions[0];
partialRow = null;
}
var lastRow = questions[questions.length - 1];
if (lastRow.charAt(lastRow.length - 1) != '\n') {
partialRow = lastRow;
}
questions.forEach(function(row, index, array) {
count++;
var fields = row.split(',');
console.log("Processing question number: " + count + " id: " + fields[0]);
var tagString = fields[1];
var regex = new RegExp(/<([^>]+)>/g);
tags.forEach(function(tag, index, array) {
var found = false;
var questionTags;
while ((questionTags = regex.exec(tagString)) != null) {
var currentTag = questionTags[1]
if (currentTag === tag) {
found = true;
break;
}
};
//This is where the writestream is written to
if (found) {
writable = fvfileStream.write("1,", "utf8");
}else {
writable = fvfileStream.write("0,","utf8");
}
});
});
fvfileStream.write("\n");
}
qfileStream.on('readable', function() {
if (writable) {
doRead();
} else {
//Waiting for drain event.
fvfileStream.once('drain', test);
}
});
qfileStream.on('end', function() {
fvfileStream.end();
});
});
Updated
Based on advise provided by #loganfsmyth, I implemented transform streams, but still ran into the same issue. Here is my updated code:
var fs = require('fs');
var stream = require('stream');
var util = require('util');
var Transform = stream.Transform;
function FVCreator(options) {
// allow use without new
if (!(this instanceof FVCreator)) {
return new FVCreator(options);
}
// init Transform
Transform.call(this, options);
}
util.inherits(FVCreator, Transform);
var partialRow = null;
var count = 0;
var tags;
FVCreator.prototype._transform = function(chunk, enc, cb) {
var that = this;
var questions = chunk.toString().split('\n');
if (partialRow != null) {
questions[0] = partialRow + questions[0];
partialRow = null;
}
var lastRow = questions[questions.length - 1];
if (lastRow.charAt(lastRow.length - 1) != '\n') {
partialRow = lastRow;
questions.splice(questions.length - 1, 1);
}
questions.forEach(function(row, index, array) {
count++;
var fields = row.split(',');
console.log("Processing question number: " + count + " id: " + fields[0]);
var tagString = fields[1];
var regex = new RegExp(/<([^>]+)>/g);
tags.forEach(function(tag, index, array) {
var found = false;
var questionTags;
while ((questionTags = regex.exec(tagString)) != null) {
var currentTag = questionTags[1]
if (currentTag === tag) {
found = true;
break;
}
};
if (found) {
that.push("1,", "utf8");
} else {
that.push("0,", "utf8");
}
});
});
this.push("\n", "utf8");
cb();
};
fs.readFile('/another/file/to/be/read', {
encoding: "utf8"
}, function(err, data) {
if (err) throw err;
//Make an array containing tags.
tags = data.split('\n');
//write to a file.
var fvfileStream = fs.createWriteStream('/path/TagFeatureVectors.csv');
//read in the question posts
var qfileStream = fs.createReadStream('/large/file/to/be/read', {
encoding: "utf8"
});
var fvc = new FVCreator();
qfileStream.pipe(fvc).pipe(fvfileStream);
});
I am running this on OSX Yosemite.

Related

Trying to add data in unsupported state [duplicate]

This question already has answers here:
Trying to add data in unsupported state at Cipher.update
(3 answers)
Closed 2 years ago.
This is the code of my site. First time I run, it worked and showed data but the other times it shows the error "Trying to add data in unsupported state" Please help me.
var connection = require("./dbconnection");
var keys = require('./keys');
var mysql = require('mysql');
var crypto = require('crypto');
var algoritm = "aes-256-ctr";
var nameCipher = crypto.createCipher(algoritm, keys.name);
var passwordCipher = crypto.createCipher(algoritm, keys.password);
var nameDecipher = crypto.createDecipher(algoritm, keys.name);
var passwordDecipher = crypto.createCipher(algoritm, keys.password);
function encrypt(text, type) {
if (type === 1) {
var nameC = nameCipher.update(text, "utf8", "hex");
nameC += nameCipher.final("hex");
return nameC;
} else if (type === 2) {
var passwordC = passwordCipher.update(text, "utf8", "hex");
passwordC += passwordCipher.final("hex");
return passwordC;
}
}
function decrypt(text, type) {
if (type === 1) {
var nameDec = nameDecipher.update(text, "hex", "utf8");
nameDec += nameDecipher.final("utf8");
return nameDec;
} else if (type === 2) {
var passwordDec = passwordDecipher.update(text, "hex", "utf8");
passwordDec += passwordDecipher.final("utf8");
return passwordDec;
}
}
function logIn(name, password) {
var eName = encrypt(name, 1);
var ePassword = encrypt(password, 2);
var dData;
console.dir(eName);
console.dir(ePassword);
connection.getConnection(function (err, connection) {
connection.query("SELECT * FROM students WHERE name=\"" + eName + "\" AND password=\"" + ePassword + "\"",
function (err, data) {
if (err) throw err;
console.dir(data);
dData = data;
})
})
}
exports.logIn = logIn;
Always create a new cipher before updating. You should create them inside the encrypt and decrypt functions.
function encrypt(text, type) {
var nameCipher = crypto.createCipher(algoritm, keys.name); //<---- here
var passwordCipher = crypto.createCipher(algoritm, keys.password); //<---- here
if (type === 1) {
var nameC = nameCipher.update(text, "utf8", "hex");
nameC += nameCipher.final("hex");
return nameC;
} else if (type === 2) {
var passwordC = passwordCipher.update(text, "utf8", "hex");
passwordC += passwordCipher.final("hex");
return passwordC;
}
}

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');

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

how to go from net module to ws module

here is my current code
'use strict';
var _ = require('underscore'),
util = require('util'),
events = require('events'),
net = require('net'),
colors = require('colors');
// Chatango Socket connection handler, for both Rooms and PM
// Available events: onconnect, data, error, timeout, close, write ( Note: exceptions must be handled! )
function Socket(host, port)
{
this._host = host;
this._port = port || 443;
this._socket = new net.Socket();
this._pingTask = false;
this._connected = false;
this._firstCommand = true;
this._writeLock = false;
this._writeBuffer = [];
this._buffer = '';
this.connect();
}
util.inherits(Socket, events.EventEmitter);
Socket.prototype.connect = function()
{
if(this._socket._connecting) return;
var self = this;
if(this._socket.destroyed){
var reconnecting = true;
console.log('[SOCKET] reconnecting to '+this._host+':'+this._port);
}else{
var reconnecting = false;
console.log('[SOCKET] connecting to '+this._host+':'+this._port);
}
this._writeLock = true;
if(this._socket._events.connect){
this._socket.connect(this._port, this._host);
}else{
this._socket.connect(this._port, this._host, function() {
self._connected = true;
self._writeLock = false;
self._pingTask = setInterval(function() {
if(self._connected) {
self.write(['']);
}
}, 30000);
self.emit('onconnect');
});
}
if(reconnecting) return;
this._socket.on('data', function(data) {
var buffer = data.toString('utf8');
if(buffer.substr(-1) !== '\x00')
{
self._buffer += buffer;
}
else
{
if(self._buffer != '')
{
buffer = self._buffer + buffer;
self._buffer = '';
}
var messages = buffer.split('\x00');
_.each(messages, function(message){
message = message.replace(/(\r|\n)/g, '');
if(message !== '')
self.emit('data', message);
});
}
});
this._socket.on('error', function(exception) {
self.emit('error', exception);
});
this._socket.on('timeout', function(exception) {
self.emit('timeout', exception);
});
this._socket.on('close', function() {
self._connected = false;
self._writeBuffer = [];
self._writeLock = false;
self._buffer = '';
self._firstCommand = true;
clearInterval(self._pingTask);
self.emit('close');
});
}
Socket.prototype.disconnect = function(){
this._socket.destroy();
}
Socket.prototype.setWriteLock = function(bool) {
this._writeLock = _.isBoolean(bool) && bool;
}
Socket.prototype.write = function(data) {
if(this._connected)
{
if(this._firstCommand)
{
var terminator = '\x00';
this._firstCommand = false;
}
else
var terminator = '\r\n\x00';
if(this._writeLock)
this._writeBuffer.push(data);
else
{
_.each(this._writeBuffer, function(value){
this.write(value);
}.bind(this));
if(data)
this.emit('write', data.join(':'));
this._socket.write(data.join(':') + terminator);
}
}
}
exports.Instance = Socket;
as you can see i am using the net module
i want to change to the ws module and do this using websockets https://github.com/einaros/ws
i've read all kinds of examples but none of them give me what i want
the lib im using is
https://github.com/MakuraYami/derplib

Resources