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);
});
Related
I'm creating a web application that generates a pdf on a server then sends it to the client for display within the browser.
The client is using Vuejs / Axios to send a POST request. Afterwards, The server is receiving it with Expressjs, generating a unique PDF, converting the file to a base64 value then sending it back as a response.
I cannot seem to get the response correct. When I attempt to display response.data.pdfData within the client I get undefined in the console. I can see that there is indeed a response with the key and value pair using inspection tools within the Network tab under the Preview section but cannot seem to access it.
// FILE: ./Client/src/App.vue
submit(personalInfo) {
this.cardInfo.personalInfo = personalInfo;
console.log('Sending POST preview_card...');
axios({
url: 'http://localhost:5000/api/preview_card',
method: 'POST',
responseType: 'blob',
data: {
cardInfo: this.cardInfo,
},
}).then((response) => {
console.log(response.data.pdfData);
});
},
// FILE: ./Server/app.js
app.post('/api/preview_card', (req, res) => {
// Generate pdf
const doc = new jsPDF('p');
doc.setFontSize(40);
doc.text(req.body.cardInfo.templateInfo, 100, 100);
doc.save('response.pdf');
// Convert pdf to base64
var tempFile = path.resolve(__dirname, './response.pdf');
var pdfBase64 = fs.readFileSync(tempFile).toString('base64');
res.setHeader('Content-Type', 'application/json');
return res.send(JSON.stringify({ pdfData: pdfBase64 }));
});
I find it necessary to serve the pdf this way due to my client's compnents as well as to enforce a level of data coherency between concurrent users.
The Scenario
I am running a VueJs client, a NodeJs Restify API Server, and a Tika-server out of the official Docker Image. A user makes a POST call with formData containing a PDF file to be parsed. The API server receives the POST call and I save the PDF on the server. The API server should PUT the file to the unpack/all endpoint on the Tika-server and receive a zip containing a text file, a metadata file, and the set of images in the PDF. I would then process the zip and pass some data back to the client.
The Problem
I create a buffer containing the file to be parsed using let parsingData = fs.createReadStream(requestFilename); or let parsingData = fs.readFileSync(requestFilename);, set the axios data field to parsingData, then make my request. When I get the response from the Tika-server, it seems the Tika-server has treated the request as empty; within the zip, there are no images, the TEXT file is empty, the METADATA.
When I make the following request to the Tika-server via CURL curl -T pdf_w_images_and_text.pdf http://localhost:9998/unpack/all -H "X-Tika-PDFExtractInlineImages: true" -H "X-Tika-PDFExtractUniqueInlineImagesOnly: true"> tika-response.zip, I get a response zip file containing accurate text, metadata, stripped images.
The Code
let parsingData = fs.createReadStream('pdf_w_images_and_text.pdf');
axios({
method: 'PUT',
url: 'http://localhost:9998/unpack/all',
data: parsingData,
responseType: 'arraybuffer',
headers: {
'X-Tika-PDFExtractInlineImages': 'true',
'X-Tika-PDFExtractUniqueInlineImagesOnly': 'true'
},
})
.then((response) => {
console.log('Tika-server response recieved');
const outputFilename = __dirname+'\\output.zip';
console.log('Attempting to convert Tika-server response data to ' + outputFilename);
fs.writeFileSync(outputFilename, response.data);
if (fs.existsSync(outputFilename)) {
console.log('Tika-server response data saved at ' + outputFilename);
}
})
.catch(function (error) {
console.error(error);
});
The Question
How do I encode and attach my file to my PUT request in NodeJs such that the Tika-server treats it as it does when I make the request through CURL?
Axios is sending the request with a content type of application/x-www-form-urlencoded and therefore the file content isn't being detected and parsed.
You can change this by passing either the known content type of the file, or a content type of application/octet-stream to allow Apache Tika Server to auto-detect.
Below is a sample based on your question's code that illustrates this:
#!/usr/bin/env node
const fs = require('fs')
const axios = require('axios')
let parsingData = fs.createReadStream('test.pdf');
axios({
method: 'PUT',
url: 'http://localhost:9998/unpack/all',
data: parsingData,
responseType: 'arraybuffer',
headers: {
'X-Tika-PDFExtractInlineImages': 'true',
'X-Tika-PDFExtractUniqueInlineImagesOnly': 'true',
'Content-Type': 'application/octet-stream'
},
})
.then((response) => {
console.log('Tika-server response recieved');
const outputFilename = __dirname+'/output.zip';
console.log('Attempting to convert Tika-server response data to ' + outputFilename);
fs.writeFileSync(outputFilename, response.data);
if (fs.existsSync(outputFilename)) {
console.log('Tika-server response data saved at ' + outputFilename);
}
})
.catch(function (error) {
console.error(error);
});
I'm trying to upload a remote file via Slack API https://slack.com/api/files.upload using Node with axios library.
https://api.slack.com/methods/files.upload
async filesUpload(token, channel, content, filename) {
const form = new FormData()
form.append('token', token)
form.append('channels', channel)
form.append('content', content)
form.append('filename', filename)
form.append('filetype', 'auto')
const { data } = await axios.post(
'https://slack.com/api/files.upload',
form,
{
headers: form.getHeaders(),
}
)
}
// url is a publicly available remote jpg image
const { data } = await axios.get(url, {
responseType: 'blob',
})
filesUpload('XXXX', 'XXXXX', data, 'foo.jpg')
Slack API says all good, and post its content (some gibberish) to the channel and in the response I get plain text filetype:
...
mimetype: 'text/plain',
filetype: 'text',
...
I'm pretty sure is about encoding I'm sending, but I'm out of options. I was trying to downloading the file with responseType: 'blob', responseType: 'arraybuffer' but no luck.
Please help.
I didn't manage to fix it using axios, but I switched the request to https://github.com/request/request-promise and everything works fine.
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);
}
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.");
});
});