Express.js - how to download base64 string as PDF file? - node.js

I have pdf file encoded as base64 string. How to download this string to the browser as file in .pdf format?
What I have already tried:
res.set('Content-Disposition', 'attachment; filename="filename.pdf"');
res.set('Content-Type', 'application/pdf');
res.write(fileBase64String, 'base64');

I ended up to decode the pdf first and then send it to the browser as binary as follows:
(For simplicity I use node-http here but the functions are available in express as well)
const http = require('http');
http
.createServer(function(req, res) {
getEncodedPDF(function(encodedPDF) {
res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="filename.pdf"'
});
const download = Buffer.from(encodedPDF.toString('utf-8'), 'base64');
res.end(download);
});
})
.listen(1337);
What drove me nuts here was the testing with Postman:
I was using the Send Button instead of the Send and Download Button to submit the request:
Using the Send button for this request causes that the pdf file becomes corrupted after saving.

Just a reference for Express. This answer is based on ofhouse's answer.
This solution is downloading a png file. I was missing the "Content-Disposition" part, which makes the browser not display the png, but download it. png is a Buffer-object.
app.get("/image", (req, res) => {
getPng()
.then((png) => {
res.writeHead(200, {
"Content-Type": png.ContentType,
"Content-Length": png.ContentLength,
"Content-Disposition": 'attachment; filename="image.png"',
});
res.end(png.Body);
})
.catch(() => {
res.send("Couldn't load the image.");
});
});

Related

Sending pdf files to user from nodejs to reactjs

I have pdf documents stored in the file system on the server side.
I need to let the user download one of them when he/she clicks on download.
The problem is that I know how to send a file from NodeJS to browser but here the request will be made by a ReactJS axios request. So when I send a file, the response will go to react. How do I send that pdf file to the user? Do I access the file system directly using my front end code?
I get the following in the browser console when I log the response after I do res.sendFile(file_path) in NodeJS
How do I process this so that I can make the user download the pdf?
You can use file-saver to download the file. Below is the function I'm using for pdf download. (response.data is the Buffer that nodejs sends back as a response)
import FileSaver from 'file-saver';
...
_onPdfFetched() {
FileSaver.saveAs(
new Blob([response.data], { type: 'application/pdf' }),
`sample.pdf`
);
}
or you can just show pdf to the user
window.open(response.data, '_blank');
Edit
The axios call should be like this:
axios.get(url, {
responseType: 'arraybuffer',
headers: {
Accept: 'application/pdf',
},
});
Edit 2
The nodejs code should be like this:
router.post('/api/downloadfile',(req, res, next) => {
const src = fs.createReadStream('path to sample.pdf');
res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename=sample.pdf',
'Content-Transfer-Encoding': 'Binary'
});
src.pipe(res);
});

How can I display instead download pdf file in express route

I'm doing a API request that sends a PDF file as response, but when the route is accessed, the file is immediately force downloaded. I just want to display a PDF file inside a client browser, without downloading it.
exports.print = (req,res) => {
request("API").on('response', function(response) {
res.set({
'Content-Disposition': 'inline; filename=my.pdf',
'Content-Type': 'application/pdf'
});
})
.pipe(res);
}

NodeJS request fetch pdf file and save on disk but opens as blank white page

I am fetching a pdf file and want to save that on disk. Below is my code:
request.post({
url: some_api_url,
json: true,
body: {
by: user,
password: 'mypassword'
}
}, function(err, response, body) {
if (err) next(err);
else {
if (typeof(body) == 'string') {
//console.log(body);
fs.writeFileSync(path.join(__dirname, 'abc.pdf'), body, 'binary', function(err) {
console.log(err);
});
} else {
console.log("invalid file");
}
}
});
This saves the pdf on disk with the right size (about 200kb) which means that there is data in body of the post request. However, the pdf opens up blank in document viewer in Ubuntu.
I have also compared "cat abd.pdf | less" outputs of a working pdf file (which opens fine) and the one downloaded through the request and top and bottom of both are same.
Below is the api code that serves the pdf file. If I make the request in postman, the pdf file downloads and save to disk and opens up fine.
let fileStat = fs.statSync(filePath);
res.writeHead(200, {
'Content-Disposition': 'attachment; filename="report.pdf"',
'Content-Type': 'application/pdf',
'Content-Length': fileStat.size
});
let readStream = fs.createReadStream(filePath);
res.on('finish', function() {
console.log("file sent");
});
readStream.pipe(res);
Writing my solution in case anyone needs it.
The problem was in encoding while reading the file and sending it. Now I am reading the file as base64, transmitting and then again saving from base64 and now it works fine.

Using Node/Express to stream file to user for download

I want to use a Node/Express server to stream a file to the client as an attachment. I would like to make an async request from the client to a /download endpoint and then serve an object received via API proxy to the client as a downloadable file (similar to the way res.attachment(filename); res.send(body); behaves).
For example:
fetch(new Request('/download'))
.then(() => console.log('download complete'))
app.get('/download', (req, res, next) => {
// Request to external API
request(config, (error, response, body) => {
const jsonToSend = JSON.parse(body);
res.download(jsonToSend, 'filename.json');
})
});
This will not work because res.download() only accepts a path to a file. I want to send the response from an object in memory. How is this possible with existing Node/Express APIs?
Setting the appropriate headers does not trigger a download, either:
res.setHeader('Content-disposition', 'attachment; filename=filename.json');
res.setHeader('Content-type', 'application/json');
res.send({some: 'json'});
This worked for me.
I use the content type octet-stream to force the download.
Tested on chrome the json was downloaded as 'data.json'
You can't make the download with ajax according to: Handle file download from ajax post
You can use a href / window.location / location.assign. This browser is going to detect the mime type application/octet-stream and won't change the actual page only trigger the download so you can wrap it a ajax success call.
//client
const endpoint = '/download';
fetch(endpoint, {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(res => {
//look like the json is good to download
location.assign(endpoint);
})
.catch(e => {
//json is invalid and other e
});
//server
const http = require('http');
http.createServer(function (req, res) {
const json = JSON.stringify({
test: 'test'
});
const buf = Buffer.from(json);
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-disposition': 'attachment; filename=data.json'
});
res.write(buf);
res.end();
}).listen(8888);
You can set the header to force the download, then use res.send
see those links
Force file download with php using header()
http://expressjs.com/en/api.html#res.set

Downloading S3 file as attachment using knox/node.js passthrough

I am trying to get downloading files from S3 via node/knox working. My javascript call is working and successfully downloads the file, but I want to download it as an attachment. I have tried setting the headers to 'Content-disposition': 'attachment; filename=myfile.zip', but it doesn't seem to be working. Here is my sample code:
var mimetype = mime.lookup(product.filename);
var headers = {
'Content-disposition': 'attachment; filename=' + product.filename,
'Content-type': mimetype
};
var get = knox.getFile(product.filename, function(err, result){
if(err) { return next(err); }
res.setHeader('Content-disposition', 'attachment; filename=' + product.filename);
res.setHeader('Content-type', mimetype);
result.pipe(res);
});
I have also tried setting those headers on the knox call, but still won't download as attachment.
So it looks like the problem wasn't my server at all, as I was unaware that you cannot use xhr (i.e. $resource with Angular) to download files as attachments. The simplest way I have found to get around this so far, is to only use xhr to validate the download, returning a token to the user which can be used non-xhr to get the actual file.

Resources