Sending data to server in Node JS for downloading a file - javascript

So I am trying to send the data from the client to server and then at the server I am creating a new File and in the router I am sending back response to download that file.
But I could not achieve it. I am using AJAX call. Following is my code:
My Ajax call on clicking a button:
$.ajax({
type: 'POST',
url: '/createDownloadFile',
data: JSON Object,
}).done(() => {
window.open('/download');
});
In express.js:
app.post('/createDownloadFile', (req, res) => {
downloadFile.createDownloadFile(req);
res.send('SUCCESS');
});
downloadFile in a JS below:
const fs = require('fs');
const path = require('path');
module.exports.createDownloadFile = (request) => {
if (request) {
let filePath;
const userID = 'xyz';
filePath = path.join(__dirname, userID.concat('.txt'));
const dataToWrite = request.body;
fs.openSync(filePath, 'w', (err) => {
if (err) throw new Error('FILE_NOT_PRESENT');
});
fs.appendFileSync(filePath, JSON.stringify(dataToWrite, null, 4), (err) => {
if (err) throw new Error('FILE_WRITE_ERROR');
});
return filePath;
}
};
Also, in my router.js file:
router.get('/download', (req, res) => {
const filePath = makeDownloadFile.createDownloadFile(req);
res.download(filePath);
});
But seems like when I invoke the AJAX call it creates the file but unable to write in the file?
What I am missing?

Related

How to use pdfmake with fastify?

PDFMake has this method for streaming a generated PDF document :
const pdfDoc = printer.createPdfKitDocument(docDefinition, options);
pdfDoc.pipe(fs.createWriteStream('document.pdf')); // send to output stream
pdfDoc.end();
And fastify has this to stream things :
fastify.get('/streams', function (request, reply) {
const fs = require('fs')
const stream = fs.createReadStream('some-file', 'utf8')
reply.header('Content-Type', 'application/octet-stream')
reply.send(stream) // send from input stream
})
How are those two working together? Preferably, PDFMake should directly stream through fastify and not buffer the entire PDF before sending it.
For reference, with Koa (or Express), I would simply do :
app.use(async (ctx, next) => {
const pdfDoc = printer.createPdfKitDocument(docDefinition, options);
res.setHeader('content-type', 'application/pdf');
res.setHeader('content-disposition', req.headers['content-disposition'] || 'inline');
res.status(200);
pdfDoc.pipe(res);
pdfDoc.end();
});
However, I cannot do reply.send(pdfDoc).
Update
Since PDFKit's PDFKit.PDFDocument type seems to be a readable stream, I have tried
fastify.get('/streams', function (request, reply) {
const pdfDoc = printer.createPdfKitDocument(docDefinition, options);
reply.header('Content-Type', 'application/pdf');
reply.header('content-disposition', 'inline');
reply.send(pdfDoc); // "Failed to load PDF document"
})
I also tried PDFKit's solution of using blob-stream :
fastify.get('/streams', function (request, reply) {
const pdfDoc = printer.createPdfKitDocument(docDefinition, options);
const stream = pdfDoc.pipe(blobStream());
reply.header('Content-Type', 'application/pdf');
reply.header('content-disposition', 'inline');
reply.send(stream); // "Failed to load PDF document"
pdfDoc.end();
})
There are no errors except in the browser.
This is my current solution :
fastify.get('/pdf', async (request, reply) => {
const pdfDoc = printer.createPdfKitDocument(...getDocumentDefinition(request));
const buffer = await new Promise((resolve, reject) => {
const chunks = [];
const stream = new Writable({
write: (chunk, _, next) => {
chunks.push(chunk);
next();
}
});
stream.once('error', (err) => reject(err));
stream.once('close', () => resolve(Buffer.concat(chunks)));
pdfDoc.pipe(stream);
pdfDoc.end();
});
reply.type('application/pdf').code(200).send(buffer);
})

axios post data successfully but failed to assign location after response success instead res.json rendered to client as html page

axios script.js file
const creatClient = async (client) => {
try {
const res = await axios({
method: 'POST',
withCredentials: true,
url: '/[url]',
data: client,
}).then(location.assign('/[newUrl]'));
} catch (error) {
console.log(error);
}
};
submitbtn.addEventListener('click', (e) => {
e.preventDefault;
const name = document.getElementById('name').value;
const phone = document.getElementById('phone').value;
const createdATT = new Date(document.getElementById('date').value);
const followUp = new Date(document.getElementById('date2').value);
const images = document.getElementById('img').value;
const insurance = document.getElementById('insurance').value;
const client = { name, phone, insurance, images, createdATT, followUp };
console.log(client);
client ? creatClient(...client) : console.log('no object created');
});
controller file
the console log for req.body [Object: null prototype] {*** the object ***}
const multer = require('multer');
const Client = require('../models/clientModel');
const multerStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public/img');
},
filename: (req, file, cb) => {
const ext = file.mimetype.split('/')[1];
cb(null, `user-${Date.now()}.${ext}`);
},
});
const multerFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image')) {
cb(null, true);
} else {
cd(console.log('select image'), false);
}
};
const upload = multer({
storage: multerStorage,
fileFilter: multerFilter,
});
exports.uploadImages = upload.single('images');
//
exports.createClients = async (req, res, next) => {
try {
if (req.file) req.body.images = req.file.filename;
const newClient = { ...req.body };
await Client.create(req.body).then(
res.status(200).json({
status: 'success',
newClient,
})
);
} catch (err) {
console.log(err);
}
};
also with postman sending request give success response with no errors
i've tried location.replace() but also it didn't work for me
and is there another trick from server to get to the desired location out from client side
then accepts a callback as a parameter.
then(() => location.assign('/[newUrl]'))

How to obtain Json data from different site

So, let's say i have /api/cat/fact.js directory.
I wanna to get JSON Data from catfact.ninja
The thing is, i can't use require() or request() package, because if i used require, it would saya Couldnt Found Module..., and if i used request one, instead of returning the JSON Data that you beable to sees in catfact.ninja, it return JSON about the api, like hostname, port, which is i don't need
/API/api/cat/fact.js:
const express = require('express');
const app = express.Router();
const request = require('request')
app.use('', (req, res) => {
const src = 'https://catfact.ninja/fact';
const facts = request({
uri: src,
hostname: 'catfact.ninja',
port: 443,
path: '/fact',
method: 'POST',
json: 'fact'
}, (error, response, body) => {
if (error) console.log(error)
console.log(body, '\n\n' + response.fact)
})
console.log(facts);
return res.jsonp(facts)
})
module.exports = app;
You are returning JSON in the wrong place. It should be returned inside of the callback function.
Here's the solution:
const express = require('express');
const request = require('request-promise')
const app = express();
app.use('', async (req, res) => {
const src = 'https://catfact.ninja/fact';
try {
const response = await request({
uri: src,
port: 443,
method: 'GET',
json: true
})
return res.jsonp(response)
} catch (err) {
return res.jsonp(err)
}
})
function startServer() {
const port = 3000
app.listen(port, () => {
console.info('Server is up on port ' + port)
})
app.on('error', (err) => {
console.error(err)
process.exit(1)
})
}
startServer()
TIP: I suggest using request-promise npm package instead of request package as it provides async-await approach, which is cleaner. Else, you can continue using callback function as second request() function parameter.

Download JSON file from a node express server through my frontend

I have stored the file after uploading it to the downloads folder in my project directory.
I want to download that saved file from the frontend.
When I click on the download button, it doesn't fetch the file.
And when I go to http://localhost:5000/download on the express app, I got this error message
Error: Can't set headers after they are sent.
Express Server Code:
app.get('/download', (req, res) => {
res.send('file downloaded')
const file = './downloads/output.yml';
res.download(file, 'openapi.yml', (err) => {
if (err) {
console.log(err)
} else {
console.log('file downloaded')
}
});
});
Frontend App code:
HTML:
<button class="download-btn">download</button>
Script:
const handleDownload = async () => {
const res = await fetch("https://cors-anywhere.herokuapp.com/http://localhost:5000/download");
const blob = await res.blob();
download(blob, 'output.yml');
}
downloadBtn.addEventListener('click', handleDownload);
Folder Structure:
Update:
Server.js
const uploadFiles = async (req, res) => {
const file = await req.files[0];
console.log(file)
postmanCollection = file.path;
outputFile = `downloads/${file.filename}.yml`
convertCollection();
res.json({ message: "Successfully uploaded files" });
}
app.post("/upload_files", upload.array("files"), uploadFiles);
Anyone please help me with this.
You are already using res.send ,which sends the response headers back to client ,which ends the request response cycle ,and when you try to do res.download it throws error. Use instead
app.get('/download', (req, res) => {
const file = './downloads/output.yml';
res.download(file, 'openapi.yml', (err) => {
if (err) {
console.log(err)
} else {
console.log('file downloaded')
}
});
});
res.send('file downloaded')--->remove this line
You need to update your js code as well
const handleDownload = async () => {
const res = await fetch("https://cors-anywhere.herokuapp.com/download"); //http://localhost:5000--->this is not required
const blob = await res.blob();
download(blob, 'output.yml');
}
downloadBtn.addEventListener('click', handleDownload);

NodeJS - 'Error: Invalid URI "/"'

I'm using request-promise to request two JSON data files which exist locally in my local project directory folder.
ie:
However, I am getting a 500 internal server error, when trying to pass the data to the view and my node console outputs 'Error: Invalid URI "/"',
Please see below:
server.js
let express = require('express');
let app = express();
let path = require('path');
const rp = require("request-promise");
//STORE PATH for local JSON files on variables
let guest = require('./public/data/Companies');
let hotel = require('./public/data/Guests');
app.set("port", process.env.PORT || 5000);
//GET JSON
//Question: Is it okay to pass uri:guest
app.get('/data', function(req, res) {
Promise.all([rp({uri: guest, json: true}), rp({uri: hotel, json: true})]).then(function([hotels, guests]) {
//res.json({hotels, guests});
res.send({hotels, guests});
console.log(hotels, guests);
}).catch(function(err) {
console.log(err);
res.status(500).end();
});
});
//CATCHALL
app.get("/*", function(req,res){
let file = req.params[0] || "/views/index.html";
res.sendFile(path.join(__dirname, "/public/", file));
});
//SET PORT
app.listen(app.get("port"), function(){
console.log("Listening on port: " , app.get("port"));
});
then on client.js:
$(function() {
$.ajax({
type: "GET",
url: "/data",
success: function (res) {
console.log(res);
}
});
});
Why do you use request to get the data? Why don't you use the filesystem module from Node.js (fs) to get the data? When you call rp(), you should pass an absolute URI and not a local path.
To use it in your code, you need to "promisify" the readFile function:
let readFileAsAPromise = function(filename){
return new Promise(function(resolve, reject) {
fs.readFile(filename, (data, err) => {
if(err) reject(err);
resolve(data)
})
})
}
You can then you Promise.all.
Why aren't you simply returning the variables?
I mean:
app.get('/data', function(req, res) {
res.send({hotels, guests});
});

Categories

Resources