Json doesnt save properly using node.js - node.js

I am connected to websocket, each time i get message i save its content to a json file.If i get two or more messages in the same second it doesnt save it properly.How can i prevent that ?Each time i get message I am using :
fs.readFile(bought_path,'utf-8',(err,data) =>{ ...
//do something
to read json file , and
fs.writeFile(bought_path, JSON.stringify(kupljeni_itemi) , 'utf-8');
to save edited json file.

One way to guard is to make a simple locking mechanism:
let isLocked = false; // declare it in an upper scope.
if (!isLocked) { // check if it is not locked by other socket call.
isLocked = true; // set the lock before writing the content
fs.writeFile(file, json, (err) => {
isLocked = false; // unlock when you get the response
})
}

you could use synchronous read/write functions -
readFileSync and writeFileSync

Related

How do I stream a chunked file using Node.js Readable?

I have a 400Mb file split into chunks that are ~1Mb each.
Each chunk is a MongoDB document:
{
name: 'stuff.zip',
index: 15,
buffer: Binary('......'),
totalChunks: 400
}
I am fetching each chunk from my database and then streaming it to the client.
Every time I get chunk from the DB I push it to the readableStream which is being piped to the client.
Here is the code:
import { Readable } from 'stream'
const name = 'stuff.zip'
const contentType = 'application/zip'
app.get('/api/download-stuff', (req, res) => {
res.set('Content-Type', contentType)
res.set('Content-Disposition', `attachment; filename=${name}`)
res.attachment(name)
// get `totalChunks` from random chunk
let { totalChunks } = await ChunkModel.findOne({ name }).select('totalChunks')
let index = 0
const readableStream = new Readable({
async read() {
if (index < totalChunks) {
let { buffer } = await ChunkModel.findOne({ name, index }).select('buffer')
let canContinue = readableStream.push(buffer)
console.log(`pushed chunk ${index}/${totalChunks}`)
index++
// sometimes it logs false
// which means I should be waiting before pushing more
// but I don't know how
console.log('canContinue = ', canContinue)
} else {
readableStream.push(null)
readableStream.destroy()
console.log(`all ${totalChunks} chunks streamed to the client`)
}
}
})
readableStream.pipe(res)
})
The code works.
But I'm wondering whether I risk having memory overflows on my local server memory, especially when the requests for the same file are too many or the chunks are too many.
Question: My code is not waiting for readableStream to finish reading the chunk that was just pushed to it, before pushing the next one. I thought it was, and that is why I'm using read(){..} in this probably wrong way. So how should I wait for each chunk to be pushed, read, streamed to the client and cleared from my server's local memory, before I push the next one in ?
I have created this sandbox in case it helps anyone
In general, when the readable interface is implemented correctly (i.e., the backpressure signal is respected), the readable interface will prevent the code from overflowing the memory regardless of source size.
When implemented according to the API spec, the readable itself does not keep references for data that has finished passing through the stream. The memory requirement of a readable buffer is adjusted by specifying a highWatermark.
In this case, the snippet does not conform to the readable interface. It violates the following two concepts:
No data shall be pushed to the readable's buffer unless read() has been called. Currently, this implementation proceeds to push data from DB immediately. Consequently, the readable buffer will start to fill before the sink has begun to consume data.
The readable's push() method returns a boolean flag. When the flag is false, the implementation must wait for .read() to be called before pushing additional data. If the flag is ignored, the buffer will overflow wrt. the highWatermark.
Note that ignoring these core criteria of Readables circumvents the backpressure logic.
An alternative implementation, if this is a Mongoose query:
app.get('/api/download-stuff', async (req, res) => {
// ... truncated handler
// A helper variable to relay data from the stream to the response body
const passThrough = new stream.PassThrough({objectMode: false});
// Pipe data using pipeline() to simplify handling stream errors
stream.pipeline(
// Create a cursor that fetch all relevant documents using a single query
ChunkModel.find().limit(chunksLength).select("buffer").sort({index: 1}).lean().cursor(),
// Cherry pick the `buffer` property
new stream.Transform({
objectMode: true,
transform: ({ buffer }, encoding, next) => {
next(null, buffer);
}
}),
// Write the retrieved documents to the helper variable
passThrough,
error => {
if(error){
// Log and handle error. At this point the HTTP headers are probably already sent,
// and it is therefore too late to return HTTP500
}
}
);
res.body = passThrough;
});

How to use Transform stream to validate data in Node.js?

I want to pipe data from my readable stream to a writable stream but validate in between.
In my case:
Readable Stream: http response as a stream (Axios.post response as a stream to be more specific)
Writable Stream: AWS S3
Axios.post response comes in XML format. So, it means the readable stream will read chunks that represent XML. I transform each chunk to string and check if <specificTag> (opening) and </specificTag> closing is available. Both these checks will be done in different or arbitrary chunks.
If both opening/closing tags are OK then I have to transfer the chunk to Writable stream.
I am coding like:
let openTagFound: boolean: false;
let closingTagFound: boolean: false;
readableStream.pipe(this.validateStreamData()).pipe(writableStream);
I have also defined _tranform method for validateStreamData() like:
private validateStreamData(): Transform {
let data = '', transformStream = new Transform();
let openTagFound: boolean = false;
let closingTagFound: boolean = false;
try {
transformStream._transform = function (chunk, _encoding, done) {
// Keep chunk in memory
data += chunk.toString();
if(!openTagFound) {
// Check whether openTag e.g <specificTag> is found, if yes
openTagFound = true;
}
if(!closingTagFound) {
// parse the chunk using parser
// Check whether closingTag e.g </specificTag> is found, if yes
closingTagFound = true;
}
// we are not writing anything out at this
// time, only at end during _flush
// so we don't need to call push
done();
};
transformStream._flush = function (done) {
if(openTagFound && closingTagFound) {
this.push(data);
}
done();
};
return transformStream;
} catch (ex) {
this.logger.error(ex);
transformStream.end();
throw Error(ex);
}
}
Now, you can see that I am using a variable data at:
// Keep chunk in memory
data += chunk.toString();
I want to get rid of this. I do not want to utilize memory explicitly. The final goal is to get data from Axios.post and transfer it to AWS S3, only if my validation succeeds. If not, then it should not write to S3.
Any help is much appreciated.
Thanks in Advance!!!
So, What I finally did is, let the pipe end and kept some flags to check whether it is valid or invalid and then on('end') callback, if flag says invalid explicitly destroyed destination object.

Nodejs streams - convert errors into default values

I'm quite unfamiliar with streaming. Suppose I have an input stream:
let inputStream = fs.createReadStream(getTrgFilePath());
I'm going to pipe this input to some output stream:
inputStream.pipe(someGenericOutputStream);
In my case, getTrgFilePath() may not produce a valid filepath. This will cause a failure which will result in no content being sent to someGenericOutputStream.
How do I set things up so that when inputStream encounters an error, it pipes some default value (e.g. "Invalid filepath!") instead of failing?
Example 1:
If getTrgFilePath() is invalid, and someGenericOutputStream is process.stdout, I want to see stdout say "Invalid filepath!"
Example 2:
If getTrgFilePath() is invalid, and someGenericOutputStream is the result of fs.createOutputStream(outputFilePath), I would expect to find a file at outputFilePath with the contents "Invalid filepath!".
I'm interested in a solution which doesn't need to know what specific kind of stream someGenericOutputStream is.
If you are only worried about the path being invalid, you could first check the output with fs.access, but as I understand you don't want additional "handling" code in your file...
So let's take into account what may go wrong:
File path is not valid,
File or path does not exist,
File is not readable,
File is read but something happens when it fails.
Now I'm gonna leave the 4th case alone, this is a separate case, so we'll just ignore such a situation. We need two files (so that your code looks clean and all the mess is in a separate file) - here's the, lets say, ./lib/create-fs-with-default.js file:
module.exports = // or export default if you use es6 modules
function(filepath, def = "Could not read file") {
// We open the file normally
const _in = fs.createReadStream(filepath);
// We'll need a list of targets later on
let _piped = [];
// Here's a handler that end's all piped outputs with the default value.
const _handler = (e) => {
if (!_piped.length) {
throw e;
}
_piped.forEach(
out => out.end(def)
);
};
_in.once("error", _handler);
// We keep the original `pipe` method in a variable
const _orgPipe = _in.pipe;
// And override it with our alternative version...
_in.pipe = function(to, ...args) {
const _out = _orgPipe.call(this, to, ...args);
// ...which, apart from calling the original, also records the outputs
_piped.push(_out);
return _out;
}
// Optionally we could handle `unpipe` method here.
// Here we remove the handler once data flow is started.
_in.once("data", () => _in.removeListener("error", _handler));
// And pause the stream again so that `data` listener doesn't consume the first chunk.
_in.pause();
// Finally we return the read stream
return _in;
};
Now there's just a small matter to use it:
const createReadStreamWithDefault = require("./lib/create-fs-with-default");
const inputStream = fs.createReadStream(getTrgFilePath(), "Invalid input!");
// ... and at some point
inputStream.pipe(someOutput);
And there you go.
M.

Processing a 100MB file in Node.js

Basically I have a file, say, 100mb.qs and I need to pass its entire contents through the following function:
function process(in){
var out = JSON.stringify(require('querystring').parse(in));
return out;
}
And then replace the file's contents with the result.
I imagine that I'll have to stream it, so...
require('fs').createReadStream('1mb.qs').pipe( /* ??? */ )
What to I do?
You should take a look at clarinet for parsing JSON as a stream.
var createReadStream = require('fs').createReadStream
, createWriteStream = require('fs').createReadStream
, parseJson = require('clarinet').createStream()
;
parseJson.on('error', function(err){
if (err) throw err
})
parseJson.on('onvalue', function(v){
// do stuff with value
})
parseJson.on('onopenobject', function (key) {
// I bet you got the idea how this works :)
})
createReadStream('100mb.qs')
.pipe(parseJson)
.pipe(createWriteStream('newerFile.qs'))
there is many more events to listen to, so you definitely show take a look.
Also, it will send data down stream whenever a JSON node is ready to be written. It couldn't get better then this.
Hope this helps

Node js- writing data to the writable stream

In my node application im writing data to the file using write method in the createWriteStream method.Now i need to find whether the write for the particular stream is complete or not.How can i find that.
var stream = fs.createWriteStream('myFile.txt', {flags: 'a'});
var result = stream.write(data);
writeToStream();
function writeToStream() {
var result = stream.write(data + '\n');
if (!result) {
stream.once('drain',writeToStream());
}
}
I need to call other method for every time when write completes.How can i do this.
From the node.js WritableStream.write(...) documentation you can give the "write" method a callback that is called when the written data is flushed:
var stream = fs.createWriteStream('myFile.txt', {flags: 'a'});
var data = "Hello, World!\n";
stream.write(data, function() {
// Now the data has been written.
});
Note that you probably don't need to actually wait for each call to "write" to complete before queueing the next call. Even if the "write" method returns false you can still call subsequent writes and node will buffer the pending write requests into memory.
I am using maerics's answer along with error handling. The flag 'a' is used to Open file for appending. The file is created if it does not exist. There Other flags you can use.
// Create a writable stream & Write the data to stream with encoding to be utf8
var writerStream = fs.createWriteStream('MockData/output.txt',{flags: 'a'})
.on('finish', function() {
console.log("Write Finish.");
})
.on('error', function(err){
console.log(err.stack);
});
writerStream.write(outPutData,function() {
// Now the data has been written.
console.log("Write completed.");
});
// Mark the end of file
writerStream.end();

Resources