Saving API data to JSON file with NodeJS (or PHP) - javascript

Is there a way to save data from an API to a JSON file, with NodeJS using XMLHttpRequest?
The API data is supposed to be displayed on a website, but the API is increcibly slow, so to combat this I would save the data on the server and display the newest data on the website every 5 minutes.
The API is public, the link is http://lonobox.com/api/index.php?id=100002519 if that helps.
Any help is greatly appreciated.

Hey I do a similar thing with a node server that performs basic function on JSON data that I use at work. When it comes to saving the data I just POST it to the server.
But when it come to reading the data I use a XMLHttpRequest to do it, let me illustrate how it works which should give you a good start.
POST file to server.
function processFile(e) {
var file = e.target.result,results;
if (file && file.length) {
$.ajax({
type: "POST",
url: "http://localhost:8080/",
data: {
'data': file
}
}).done(function(msg) {
appendText("Data Saved: " + msg);
});
}
}
From here you can fetch the data with XMLHttpRequest like so...
function getFile(){
var rawFile = new XMLHttpRequest();
rawFile.open("GET", "filename.json", false);
rawFile.onreadystatechange = function ()
{
if(rawFile.readyState === 4)
{
if(rawFile.status === 200 || rawFile.status == 0)
{
var fileText = rawFile.responseText;
}
}
}
rawFile.send(null);
}
Server Code
app.post('/', function(req, res) {
var fileLoc = __dirname.split("\\").length > 1 ? __dirname + "\\public\\filename.json" : __dirname + "/public/filename.json";
fs.writeFile(fileLoc, req.body.data, function(err) {
if (err) {
res.send('Something when wrong: ' + err);
} else {
res.send('Saved!');
}
})
});
Server side requires FS and I use Express for routing.

Related

Page reloads on completion of AJAX request using NodeJS

I am trying to use a get request to run a python script and return a result using AJAX, Nodejs (using express) and Python-Shell (which runs a server side python script). I'm wanting this result to be returned into a div on my page.
The code does return a result from the python script back to into the web page (it flashes up for a second in the div), but then almost immediately, the page reloads.
My client side JS Function (which is triggered through a button onclick event)
function getResult() {
xmlhttp = new XMLHttpRequest()
ResultCoords = encodeURI('oLat=' + document.getElementById('originLat').innerHTML + '&oLng=' + document.getElementById('originLng').innerHTML + '&dLat=' + document.getElementById('destLat').innerHTML + '&dLng=' + document.getElementById('destLng').innerHTML);
xmlhttp.open("GET", "http://localhost:3000/run?" + ResultCoords, false);
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
string = xmlhttp.responseText;
document.getElementById('result').innerHTML = string
}
}
xmlhttp.send(ResultCoords)
}
My server side script
router.get('/run?*', (req, res) => {
let options = {
mode: 'text',
pythonPath: 'my python path',
pythonOptions: ['-u'],
scriptPath: 'my script path',
args: [req.query.oLat, req.query.oLng, req.query.dLat, req.query.dLng]
};
PythonShell.run('pythonScript.py', options, function (err, results) {
if (err) throw err;
console.log(results);
const data = results;
return res.send(data)
});
});
I'm still very much a beginner with express, AJAX etc. so i'm open to any suggestions on improving the code if this isn't the right way to be doing these sort of things etc.
Thanks!

NodeJS trying to use a URL inside of a fs.createfilestream

I'm trying to do a post request onto my api, the api works perfectly ( I am able to post files, but not through a url), but now I'm trying to post through an url.
this is the code I have now, I removed some lines that aren't relevant to the question or were for testing.
request({
url: url + "gettoken"
, json: true
}, function (error, response, body) {
user = body;
var rs = fs.createReadStream(up.url);
var ws = request.post(url + "upload?token=" + `${user.token}&key=${user.key}&filename=${filename}`);
ws.on('drain', function () {
rs.resume();
});
rs.on('end', function () {
console.log(filename);
});
ws.on('error', function (err) {
console.error('cannot send file ' + err);
});
rs.pipe(ws);
})
Can anyone please help me.
So the idea is to upload a file that's located at up.url to another server at url + "upload?...".
Since fs.createReadStream is meant to read local files, and not URL's, you need something that can create a stream from a URL (or rather, retrieve that URL and stream the response).
You can also use request for that:
request({
url: url + "gettoken",
json: true
}, function (error, response, body) {
const user = body;
const rs = request.get(up.url);
const ws = request.post(url + "upload?token=" + `${user.token}&key=${user.key}&filename=${filename}`);
rs.on('end', function () {
console.log(filename);
});
ws.on('error', function (err) {
console.error('cannot send file ' + err);
});
rs.pipe(ws);
});
Typically, file uploads work through multipart/form-data, but your code doesn't suggest that being used here. If it is, the code would become something like this:
const ws = request.post(url + "upload?token=" + `${user.token}&key=${user.key}&filename=${filename}`, {
formData : {
the_file : rs
}
});
// no `rs.pipe(ws)`

How to handle JSON data from XMLHttpRequest POST, using nodeJS

Overarching goal is to save some JSON data I create on a webpage to my files locally. I am definitely sending something to the server, but not in format I seem to able to access.
JsonData looks like:
{MetaData: {Stock: "UTX", Analysis: "LinearTrend2"}
Projections: [2018-10-12: 127.62, 2018-10-11: 126.36000000000001, 2018-10-10: 132.17, 2018-10-09: 140.12, 2018-10-08: 137.73000000000002, …]}
XMLHttpRequest on my webpage:
function UpdateBackTestJSON(JsonUpdate){ //JsonUpdate being the JSON object from above
var request = new XMLHttpRequest();
request.open('POST', 'UpdateBackTestJSON');
request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
// request.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
request.onload = function() {
console.log("Updated JSON File");
};
console.log("about to send request");
console.log(JsonUpdate);
request.send(JSON.stringify(JsonUpdate));
}
and I handle posts on my server (rather carelessly I realize, just going for functionality as a start here)
var http = require('http')
, fs = require('fs')
, url = require('url')
, port = 8008;
var server = http.createServer (function (req, res) {
var uri = url.parse(req.url)
var qs = require('querystring');
if (req.method == 'POST'){
var body = '';
req.on('data', function (data){
body += data;
// 1e6 === 1 * Math.pow(10, 6) === 1 * 1000000 ~~~ 1MB
if (body.length > 1e6){
// FLOOD ATTACK OR FAULTY CLIENT, NUKE REQUEST
req.connection.destroy();
}
});
req.on('end', function () {
var POST = qs.parse(body);
console.log(POST); // PARSED POST IS NOT THE RIGHT FORMAT... or something, idk whats going on
UpdateBackTestData(POST);
});
}
function UpdateBackTestData(TheJsonData){
console.log("UpdateBackTestData");
console.log(TheJsonData);
JsonUpdate = JSON.parse(TheJsonData);
console.log(JsonUpdate["MetaData"]);
//var Stock = JsonUpdate["MetaData"]["Stock"];
// var Analysis = JsonUpdate["MetaData"]["Analysis"];
fs.writeFile("/public/BackTestData/"+Analysis+"/"+Stock+".json", TheJsonData, function(err){
if(err){
console.log(err);
}
console.log("updated BackTest JSON!!!");
});
}
Most confusing to me is that when I run this, the Json object Im am trying to pass, does go through to the server, but the entirety of the data is a string used as a key for a blank value in an object. when I parse the body of the POST, I get: {'{MetaData:{'Stock':'UTX','Analysis:'LinearTrend2'},'Projections':[...]}': ''}. So my data is there... but not in a practical format.
I would prefer not to use express or other server tools, as I have a fair amount of other services set up in my server that I don't want to go back and change if I can avoid it.
Thanks for any help

Why do my signed URLs get an error 403?

Reference: https://googlecloudplatform.github.io/google-cloud-node/#/docs/storage/0.8.0/storage/file?method=getSignedUrl
This is extremely strange. I did set my service account as having read permission of the storage objects.
What is going on ?
server:
snapshot.forEach(function(childSnapshot){
titleArray.push(childSnapshot.val().title);
usernameArray.push(childSnapshot.val().username);
keyArray.push(childSnapshot.key);
var file = bucket.file(childSnapshot.val().image);
var config = {
action: 'read',
expires: Date.now() + 10000,
contentType: 'image/png'
};
file.getSignedUrl(config, function(err, url) {
if (err) {
console.error(err);
return;
}
imageArray.push(url);
if (imageArray.length == 9) {
res.render("home", {keyArray: keyArray, titleArray: titleArray, usernameArray: usernameArray, imageArray: imageArray});
}
});
});
client:
$(".homeImage").each(function(i) {
var row = $(this)
row.attr('id', i);
if (i == 4) {
} else {
$("#"+i).css('background-image', "url('" + imageArray[i] + "')");
}
});
Response:
This is extremely strange since I thought signed URLs were supposed to authenticate my request as being sent by my service account.
The error message implies that your request doesn't have any authentication associated with it. For a Signed URL that would mean that the GoogleAccessId/Signature/Expires query parameters are not set.
I'd debug print the image array server side and client side to see where it is getting lost.
Edit: In this case it looks like & was being replaced with & somewhere.

Uploading a file with FormData and multer

I have successfully managed to upload a file to a Node server using the multer module by selecting the file using the input file dialog and then by submitting the form, but now I would need, instead of submitting the form, to create a FormData object, and send the file using XMLHttpRequest, but it isn't working, the file is always undefined at the server-side (router).
The function that does the AJAX request is:
function uploadFile(fileToUpload, url) {
var form_data = new FormData();
form_data.append('track', fileToUpload, fileToUpload.name);
// This function simply creates an XMLHttpRequest object
// Opens the connection and sends form_data
doJSONRequest("POST", "/tracks/upload", null, form_data, function(d) {
console.log(d);
})
}
Note that fileToUpload is defined and the url is correct, since the correct router method is called. fileToUpload is a File object obtained by dropping a file from the filesystem to a dropzone, and then by accessing the dataTransfer property of the drop event.
doJSONRequest is a function that creates a XMLHttpRequest object and sends the file, etc (as explained in the comments).
function doJSONRequest(method, url, headers, data, callback){
//all the arguments are mandatory
if(arguments.length != 5) {
throw new Error('Illegal argument count');
}
doRequestChecks(method, true, data);
//create an ajax request
var r = new XMLHttpRequest();
//open a connection to the server using method on the url API
r.open(method, url, true);
//set the headers
doRequestSetHeaders(r, method, headers);
//wait for the response from the server
r.onreadystatechange = function () {
//correctly handle the errors based on the HTTP status returned by the called API
if (r.readyState != 4 || (r.status != 200 && r.status != 201 && r.status != 204)){
return;
} else {
if(isJSON(r.responseText))
callback(JSON.parse(r.responseText));
else if (callback !== null)
callback();
}
};
//set the data
var dataToSend = null;
if (!("undefined" == typeof data)
&& !(data === null))
dataToSend = JSON.stringify(data);
//console.log(dataToSend)
//send the request to the server
r.send(dataToSend);
}
And here's doRequestSetHeaders:
function doRequestSetHeaders(r, method, headers){
//set the default JSON header according to the method parameter
r.setRequestHeader("Accept", "application/json");
if(method === "POST" || method === "PUT"){
r.setRequestHeader("Content-Type", "application/json");
}
//set the additional headers
if (!("undefined" == typeof headers)
&& !(headers === null)){
for(header in headers){
//console.log("Set: " + header + ': '+ headers[header]);
r.setRequestHeader(header, headers[header]);
}
}
}
and my router to upload files is the as follows
// Code to manage upload of tracks
var multer = require('multer');
var uploadFolder = path.resolve(__dirname, "../../public/tracks_folder");
function validTrackFormat(trackMimeType) {
// we could possibly accept other mimetypes...
var mimetypes = ["audio/mp3"];
return mimetypes.indexOf(trackMimeType) > -1;
}
function trackFileFilter(req, file, cb) {
cb(null, validTrackFormat(file.mimetype));
}
var trackStorage = multer.diskStorage({
// used to determine within which folder the uploaded files should be stored.
destination: function(req, file, callback) {
callback(null, uploadFolder);
},
filename: function(req, file, callback) {
// req.body.name should contain the name of track
callback(null, file.originalname);
}
});
var upload = multer({
storage: trackStorage,
fileFilter: trackFileFilter
});
router.post('/upload', upload.single("track"), function(req, res) {
console.log("Uploaded file: ", req.file); // Now it gives me undefined using Ajax!
res.redirect("/"); // or /#trackuploader
});
My guess is that multer is not understanding that fileToUpload is a file with name track (isn't it?), i.e. the middleware upload.single("track") is not working/parsing properly or nothing, or maybe it simply does not work with FormData, in that case it would be a mess. What would be the alternatives by keeping using multer?
How can I upload a file using AJAX and multer?
Don't hesitate to ask if you need more details.
multer uses multipart/form-data content-type requests for uploading files. Removing this bit from your doRequestSetHeaders function should fix your problem:
if(method === "POST" || method === "PUT"){
r.setRequestHeader("Content-Type", "application/json");
}
You don't need to specify the content-type since FormData objects already use the right encoding type. From the docs:
The transmitted data is in the same format that the form's submit()
method would use to send the data if the form's encoding type were set
to multipart/form-data.
Here's a working example. It assumes there's a dropzone with the id drop-zone and an upload button with an id of upload-button:
var dropArea = document.getElementById("drop-zone");
var uploadBtn = document.getElementById("upload-button");
var files = [];
uploadBtn.disabled = true;
uploadBtn.addEventListener("click", onUploadClick, false);
dropArea.addEventListener("dragenter", prevent, false);
dropArea.addEventListener("dragover", prevent, false);
dropArea.addEventListener("drop", onFilesDropped, false);
//----------------------------------------------------
function prevent(e){
e.stopPropagation();
e.preventDefault();
}
//----------------------------------------------------
function onFilesDropped(e){
prevent(e);
files = e.dataTransfer.files;
if (files.length){
uploadBtn.disabled = false;
}
}
//----------------------------------------------------
function onUploadClick(e){
if (files.length){
sendFile(files[0]);
}
}
//----------------------------------------------------
function sendFile(file){
var formData = new FormData();
var xhr = new XMLHttpRequest();
formData.append("track", file, file.name);
xhr.open("POST", "http://localhost:3000/tracks/upload", true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.send(formData);
}
The server side code is a simple express app with the exact router code you provided.
to post a FormData object accepted by multer the upload function should be like this:
function uploadFile(fileToUpload, url) {
var formData = new FormData();
//append file here
formData.append('file', fileToUpload, fileToUpload.name);
//and append the other fields as an object here
/* var user = {name: 'name from the form',
email: 'email from the form'
etc...
}*/
formData.append('user', user);
// This function simply creates an XMLHttpRequest object
// Opens the connection and sends form_data
doJSONRequest("POST", "/tracks/upload", null, formData, function(d) {
console.log(d);
})
}

Categories

Resources