NodeJS consuming Transfer-Encoding chunked using https.request - node.js

I'm making a GET request to an API that responds with Transfer-Encoding: chunked.
It's an https request, I've attached on('data') listener to it which then adds the response to an array.
There are around 5 chunks incoming each of them being a Buffer depending on the response. When I concat the array and try converting it to a String I get the weirdly decoded reply which looks to be encoded. I think it might be due to the request being HTTPS but I'm not sure, below's my code for decoding it
response.on('data', (data) => {
// Data is sent in binary chunks, let's add them up to an array
binaryDataArray.push(data)
}).on('end', () => {
const buffer = Buffer.concat(binaryDataArray);
try{
const formattedData = JSON.parse(buffer.toString('utf8'));
resolveRequest(formattedData);
}catch(error){
setTimeout(makeRequest, 5000);
}
});
Any help would be appreciated.

Check your res.headers['content-encoding'], the response may be encoded by gzip.
So you need to unzip the response buffer like this:
response.on('data', (data) => {
// Data is sent in binary chunks, let's add them up to an array
binaryDataArray.push(data)
}).on('end', () => {
const buffer = Buffer.concat(binaryDataArray);
zlib.gunzip(Buffer.concat(respData), function (err, decoded) {
if (err) throw err;
const formattedData = decoded.toString()
resolveRequest(formattedData)
});
});

Related

Node JS Response parsing (of S3 Select response)

I am trying to parse a response from an API which is a streamed response: but the structure looks different from any other response.
A sample can be found in python.
let body = []
response.on('data', function(chunk) {
body.push(chunk)
})
.on('end', function() {
const bff = Buffer.concat(body).toString("utf-8")
console.log( bff)
})
it returns like:
�U
:content-typeapplication/octet-stream
:event-typeRecordsJane,(949) 123-45567,Chicago,Developer
:content-typtext/xml
:event-typeStats<?xml version="1.0" encoding="UTF-8"?><Stats><BytesScanned>339</Byt:message-typeeventocessed>339</BytesProcessed><BytesReturned>39</BytesReturned></Stats>�h�8(�Ƅ�
:event-typeEndϗӒ
what is the correct way to parse such a response?
Use Bytes parsing because it is a nested message.
The spec to parse can be found here
https://docs.aws.amazon.com/AmazonS3/latest/API/RESTSelectObjectAppendix.html

How do I receive a data buffer (octet stream) on the client side in JavaScript as a file?

I have an endpoint in node/express which does something to this effect
https.get(EXTERNAL_ENDPOINT, response =>
{
const data : ArrayBuffer[] = [];
response.on("data", chunk =>
{
data.push(chunk);
});
response.on("end", () =>
{
res.setHeader("Content-type", "application/octet-stream");
return res.status(200).send(Buffer.from(data.toString(), "base64"));
});
});
I am doing this to redirect a binary data stream I receive from an external endpoint to my client.
How do I receive this binary data on the client side as a downloadable file? It can be a pdf that can be downloaded or something like an image which has to be displayed in the browser on the website (as part of the website).

Node - Making a call to an ExpressJS route, and need to do a GET request to an API in it

I'm making a cryptocurrency dashboard for a project, and I'm completely new to Node and Express. This is what I have currently
app.get('/search', function(req,res){
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(req.url);
data = []
var options = {
"method": "GET",
"hostname": "rest.coinapi.io",
"path": "/v1/assets",
"headers": {'X-CoinAPI-Key': 'MY_API_KEY_HERE'}
};
const request = https.request(options, (data, response) => {
response.on('data', d => {
data.push(d);
})
});
console.log(data);
request.end();
res.end();
})
The idea is on my front end, I have a button that when clicked, will make a request to the CoinAPI api, getting all reported assets and current values. I'm not quite sure how I'm supposed to send that response data back to my frontend as a response. So, I tried to pull the response data out of the JSON that gets returned by the https.request line. I have a data array data = [] as you can see at the top of my code.
I originally had my request set up like:
const request = https.request(options, response => {
but when I would try to push d onto data, I console logged and the data array was empty. This makes sense, the data array is out of scope of the request function, so data doesn't get updated. But when I tried to pass data into the function, I errored out.
Basically I want to be able to send the JSON data back to my front end after making the request to CoinAPI. If I do process.stdout.write(d) in my https.request callback, I do actually see the coinapi data coming back. I just don't know how to send it to the front end as part of my response.
Issues:
The use of (data, response) is incorrect. The first and only argument is response so it should be (response).
The on('data') event receives buffered data. Concatenating it to a final string is the standard usage, not appending an array.
You're missing an on('end') which you should use to output the final data or do any processing.
Using res.write you're sending a text/html content type and some content which you don't want if the goal is to output JSON which the frontend can parse and use.
Missing an error handler for the API call.
Complete updated code:
app.get('/search', function(req,res){
let str = '';
const options = {
"method": "GET",
"hostname": "rest.coinapi.io",
"path": "/v1/assets",
"headers": {'X-CoinAPI-Key': 'MY_API_KEY_HERE'}
};
const request = https.request(options, (response) => {
response.on('data', d => {
str += d;
});
response.on('end', () => {
try {
let obj = JSON.parse(str);
// do any manipulation here
res.json(obj);
} catch(e){
console.log(e);
res.status(500).json({ message: 'Something went wrong - parse error' });
}
});
});
request.end();
request.on('error', (e) => {
console.log(e);
res.status(500).json({ message: 'Something went wrong - req error' });
});
});
I added a JSON.parse() to show how you'd handle that if you wanted to do some manipulation of the data before sending it to the frontend. If you simply want to return the exact response of the coin API then use an end event like:
response.on('end', () => {
res.json(str);
});
To send JSON data back to the client as response all you need to do is :
return res.json(data);
Its as simple as this. :)

How can i get binary from image using node ftp?

I want to get binary from image to rotate then, using sharp.rotate();
I try to do this content += chunk; but dosent work.
let Client = require('ftp');
let fs = require('fs');
let sharp = require('sharp');
let path = 'users/'+userId+'/headerImage/header';
let Ftp = new Client();//create new istance of Ftp
//Start. Here we get image from server
await Ftp.on('ready', function(){
Ftp.get(path, async function(err, stream){
if(err){
res.status(400).send(err);
};
var content = '';
await stream.on('data', async (chunk) => {
content += chunk;
});
await stream.on('end', async function(){
console.log(content);
let image = await sharp(content);
await image
.rotate(90)
.toBuffer()
.then(async data => {
console.log(data);
})
.catch(error => {
console.log(error);
});
Ftp.end();
});
});
});
await Ftp.connect({
host: fileTransferProtocol.host,
port: fileTransferProtocol.port,
user: fileTransferProtocol.user,
password: fileTransferProtocol.pass
});
console: Error: [Error: Input file is missing]
I believe the problem you are having is that you are not handling the incoming data as a buffer. The stream variable inside the Ftp.get callback is of type ReadableStream. By default, stream data will be returned as Buffer objects unless you specify an encoding for the data, using the readable.setEncoding() method.
For your specific purpose, you want to handle the data as a Buffer object, since that is what the sharp function is expecting. To store the incoming data into a Buffer modify what happens on the data event.
var content = new Buffer(0);
stream.on("data", async chunk => {
content = Buffer.concat([content, chunk]);
});
Also, I don't think you are using async/await duly. The ftp module runs with callbacks and events, not promises. Appending those functions with await won't make them run synchronously.
Please check the following link to find more information about this feature:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
If you want to us async/await to handle your ftp requests try this module:
https://www.npmjs.com/package/promise-ftp
It provides an asynchronous interface for communicating with an FTP server.

How to programatically download a video?

I'm trying to programmatically download a video file from a web-server through a link.
If you'd click that link through a web-browser it would just prompt you to download the video and to provide a name for the file and then download the video properly.
I have some nodejs code that just makes an HTTP request to that link and successfully gets raw data from it and saves it to a default file video.mp4
const https = require('https');
const fs = require('fs');
https.get('https://url.tocdn.com/myvideoid', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
fs.writeFile('./video.mp4', data, (err) => console.log(err))
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
The problem is that when I try to play that file through Windows Media Player, for example, it just shows an error regarding the file format. Am I missing something obvious?
Don't use string if you're pretending to store bytes,
or application/octet-stream in my case (which is the same)
From the given snippet change:
resp.on('data', (chunk) => {
data = Buffer.concat([data, Buffer.from(chunk)]);
});
And make sure to initialize data as follows:
let data = Buffer.from([]);

Resources