Send PDF from Server to Client - javascript

I'm trying to get access to a pdf from a Google Drive. Whether this access is downloading or viewing, it doesn't really matter, it just needs to be available.
I am using Javascript and NodeJS, with express and google drive api.
I have the following function below which downloads a pdf. But silly me thought this was correct because it worked locally. Then when I deployed it I realised the target filepath no longer makes sense.
function downloadDoc (sourceID, targetName, callback) {
const dest = fs.createWriteStream(`${os.homedir()}/downloads/`+targetName);
drive.files.get(
{sourceID, alt: 'media'},
{responseType: 'stream'},
(err, res) => {
if (err) {
console.error(err);
}
res.data.on('end', () => {
console.log('Done downloading file.');
callback();
})
.on('error', err => {
console.error('Error downloading file.');
throw err;
})
.pipe(dest);
});
}
So what I need to do, is take the data (or response?) from this function and send it over to client side. I assume this is simple to do but, being a simple man, I find myself stuck. I have written this, with the intention that a user can click a link on client side, requesting this URL and calling the function to download the pdf.
app.get('/download_pdf', (req, res) => {
downloadDoc(documentID, 'docName.pdf', ()=>{
console.log("downloaded pdf");
});
res.end();
});
I'm thinking I need to change the argument provided to pipe() since obviously I can't use the filepath.
Similar questions I've checked:
Display Pdf in browser using express js
How to send a pdf file from Node/Express app to the browser
Send pdf via express to js client and download
While these questions are very related to this, I think my issue is due to a lack of understanding of callbacks or requests/responses. I want to learn this properly - not just ask for answers - but as it is, I'm becoming very pressed for time and need a solution soon.

You should be able to simply pipe the returned readable stream to the express res object (which is a writeable stream):
app.get('/download_pdf', (req, res) => {
drive.files.get({
fileId: "your-file-id-here",
alt: 'media'
})
.on('end', function () {
console.log('Done');
})
.on('error', function (err) {
console.log('Error during download', err);
})
.pipe(res);
});
Edit:
as mentioned here, drive.files.get does return a promise. So you need to change it to:
app.get('/download_pdf', (req, res) => {
drive.files.get({
fileId,
alt: 'media'
}, {
responseType: 'stream'
}).then(response => {
response.data
.on('end', function () {
console.log('Done');
})
.on('error', function (err) {
console.log('Error during download', err);
})
.pipe(res);
});
});

So I figured out a way. I'm not sure if this is bad practice but it seems to work. I distinguished between the two response objects by referring to one as response and one as res.
app.get('/download_pdf', (req, res) => {
const docId = req.query.id;
drive.files.get({
fileId: docId,
alt: 'media'
}, {
responseType: 'stream'
}).then(response => {
response.data
.on('end', function () {
console.log('Done');
})
.on('error', function (err) {
console.log('Error during download', err);
})
.pipe(res);
});
});
Posting this in the event that somebody else has a similar issue.

Related

error: 405 Method Not Allowed Using Node.js

I'm building a Weather Journal application using Node, and I keep getting this error when the POST route code is getting executed. My GET function is executed well and I retrieve the data needed then this error stops the rest of the process(saving data/updating the UI/etc)..
here is the piece of code causing the problem:
async function postData(url="",data={}){
console.log(data);
const res= await fetch (url, { //this method not allowed idk why.
method: 'POST',
headers: {
'content-type':'application/JSON'
},
credentials: 'same-origin',
body: JSON.stringify(data)
});
try{
const finalData= await res.json();
console.log(finalData);
return finalData;
} catch(error) {
console.log("error", error);
}
}
Here is the server-side code after setting up express/cors/body-parser
app.listen(port, () => {
console.log(`Now listening on port ${port}`);
});
app.get('/all', function (req,res) {
res.send(projectData)
});
app.post('/add', function(req,res){
console.log(req.body);
newEntry={
date: req.body.date,
temp: req.body.temp,
content: req.body.content
}
projectData.push(newEntry);
});
I tried to use different pieces of code I found online on the same topic but it all ended up giving me the same error, tried googling as well but didn't really help. I'm also a beginner with node, app building, or working with servers so I'm really not sure if what I stated here is gonna make it clear for y'all to help.

Better way to do something after res.download in Express JS?

I have an express get route that downloads a CSV file in the browser. That part works fine, but now I want to delete the file using fs.unlink after it is downloaded because it is being stored on the host os in the temporary directory and they build up.
If I try to run the code snipped below, it cannot complete the download because it gets deleted by unlink before it can process (I think).
router.get('/downloadCsv', function(req, res, next) {
res.download(downloadCsvPath);//download file
fs.unlink(downloadCsvPath, (err) => {//delete file from server os
if (err) {
console.error(err);
} else {
console.log('removed: ' + downloadCsvPath);
}
});
});
It gives this error:
Error: ENOENT: no such file or directory, stat '/var/folders/fw/03jxpm311vs548bvf9g_1vxm0000gq/T/<filename>.csv'
As a workaround, I added a 3 second timeout to let the file download first, then delete:
router.get('/downloadCsv', function(req, res, next) {
res.download(downloadCsvPath);//download file
setTimeout(() => {
fs.unlink(downloadCsvPath, (err) => {
if (err) {
console.error(err);
} else {
console.log(downloadCsvPath);
}
});
}, 3000);
});
This works, I get the file downloaded and then it gets deleted from the temporary directory.
But, if connection was poor and it takes longer than 3 seconds, it would probably fail again. There has got to be a better way to run this fs.unlink block of code only once the res.download finishes. I am new to express js so I don't know how to do this. I want to do something like:
res.download(path).then(() => {
//do something
});
But it does not offer this.
I want to run a function after my expressjs res.download, but I do not know a way other than setTimeout
As per Express documentation, res.download() can accept a callback function, which is invoked when the transfer is complete or when an error occurs:
router.get('/downloadCsv', (req, res, next) => {
res.download(downloadCsvPath, (err) => {
if (err) {
// Handle error, but keep in mind the response may be partially-sent
// so check res.headersSent
} else {
fs.unlink(downloadCsvPath, (err) => {
if (err) {
console.error(err);
} else {
console.log(downloadCsvPath);
}
});
}
});
});
Here is the link to the relevant section in the Express documentation.
Hope this helps.

Trying to delete using Node.js Mongoose

app.delete("/tm/v1/tasks", (req,res) => {
Task.findOneAndDelete(req.body.id, (err, car) => {
if (err){
res.status(500).json({msg: error});
}
res.status(200).json({tasks});
})
});
The above isn't working and is giving an error 404. any ideas?
please check if you have are using correct url
we can add body to delete request in postman or curl but its not possible to pass body in javascript, dart (http package), etc.
you can rewrite function as
app.post('/tm/v1/delete-tasks', (req, res) => {
// write your code here...
});

What's the issue which fails me to download data from mongodb using GridFS?

I am trying to write an application which functionality includes storing files in MongoDB. I succeeded in uploading files there, with the GridFS library, but then I failed many times trying to get access to the data. I constantly get a response with internal server error while loading files metadata with this request:
router.get('/uploads', async (req,res) => {
try {
gfs.files.find().toArray( (err, files) => {
if(!files || files.length === 0) {
return res.status(404).json({message: 'No files exisits'})
}
})
res.status(200).json(files);
} catch (error) {
return res.status(500).json({ message: "Could not find files, please try again" });
}})
content-type: application/json; charset=utf-8
The other request for downloading a certain file data ruins my whole backend and I get this error:
Proxy error: Could not proxy request /api/user/getuser from localhost:3000 to http://localhost:4000/ (ECONNREFUSED).
After that none of my requests work properly on any page.
And that's a nodejs code of that request:
router.get('/uploads/:filename', async (req,res) => {
try {
gfs.files.findOne({filename: req.params.filename}, (err, file) => {
if(!file || file.length === 0) {
return res.status(404).json({message: 'No file exisits'})
}
})
res.status(200).json(file);
} catch (error) {
return res.status(500).json({ message: "Could not find files, please try again" });
}})
GridFs configurations are next:
const conn = mongoose.createConnection(config.get('mongoURI'));
conn.once('open', () => {
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('uploads');
})
const storage = new GridFsStorage({
url: config.get('mongoURI'),
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(15, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: 'uploads'
};
resolve(fileInfo);
});
});
}
});
I assume that I missed something important in documentation, and I'm not even trying to download a whole image, I am stuck. Useful advices would be highly appreciated!
I found the issue which caused an error! While emulating my request I got this error:
[0] Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Therefore the problem was in that part of my code:
res.status(200).json(files);
Nodejs does not allow to set the header of status after the actual body of request was sent.
So all the fixes I had to do were:
res.status(200).json(files) to --> res.json(files);
I hope the solution would be useful for someone.

Download a PDF file in Node and redirect into front end React

Can you help me to download a file in Node.js and redirect page into the front end? I am using MERN stack (Mongo, Express, React, Node).
After authenticating with the Google Auth, I want to download a file in Node, then I want to redirect the page.
router.get(
'/auth/google/callback',
passportGoogle.authenticate('google', {
failureRedirect: '/',
}),
(req, res) => {
try {
var file = 'resume.pdf';
res.download(file, (err => {
if (err) {
console.log(err)
} else {
window.location.href = '/';
}
}));
}
);
I tried this code, but after downloading it's not redirecting page to front end.
(req, res) => {
try {
var file = 'resume.pdf';
res.download(file, (error => {
if (!error) {
window.location.replace("http://stackoverflow.com");
} else {
console.log(error)
}
}));
}
catch {
console.log(error)
}
Since the headers are already sent with the download response, you'll have to go different route than this.
You'll need to change the response yourself.
var data = //filedata
res.set({
Content-Type: 'text/plain',
Location: '/'
});
res.end(data);
Utilize the Location header accordingly for your redirect.
On the client, you'll want to use:
window.location.replace("/headerLocation");
You want to use this on the client after the success of your callback to your download pdf method.
The reason your getting window undefined is because you're attempting to execute this on your nodejs server. The window object exists on the client.

Categories

Resources