I have a NodeJS API which has an end-point who is responsible for receive some HTML from Quill Text Editor and then generate a .PDF file with this HTML content, but my downloaded .PDF file is always blank.
Here is my API end-point:
server.post('/exportarDocumento', checkDocumento, (req, res) => {
const documento = req.body.documento;
try {
gerarPDF(documento)
.then(pdf => {
console.log('PDF GERADO', pdf); // the output is a Buffer
res.set({ 'Content-Type': 'application/pdf', 'Content-Length': pdf.length });
res.send(pdf);
});
} catch (e) {
console.log('erro: ', e);
}
finally {
// do stuff
}});
gerarPDF() function:
async function gerarPDF(documento) {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setContent(documento, { waitUntil: 'networkidle0' });
const pdfBuffer = await page.pdf({ format: 'A4' });
await browser.close();
return pdfBuffer; };
And my axios call in ReactJS to the endpoint:
await axios.post('http://localhost:3000/exportarDocumento', { documento: this.props.text })
.then(response => {
const blob = new Blob([response.data], { type: 'application/pdf' });
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'Documento.pdf';
link.click();
});
The .PDF file is being downloaded, but is always blank. Does anyone know what I'm doing wrong?
Related
How to download a file from an API response which is in a different format ?
await axios.get(`/dashboard/reportdownload/?file_name=242424242.pdf`,{
headers: {
"Authorization": `Bearer 767958756576576576jgjhgjhg`
}
})
.then((res) => {
console.log(res?.data);
});
In response I am getting
How do download it with reactjs
You need to check if the response from the API is of type blob, then convert it. For example, if it is clear you are expecting a PDF response, try this:
await axios.get(`/dashboard/reportdownload/?file_name=242424242.pdf`, {
headers: {
'Authorization': `Bearer 767958756576576576jgjhgjhg`
}
})
.then((response) => {
let fileName = '242424242.pdf';
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
// IE variant
window.navigator.msSaveOrOpenBlob(
new Blob([response.data], {
type: 'application/pdf',
encoding: 'UTF-8'
}),
fileName
);
} else {
const url = window.URL.createObjectURL(
new Blob([response.data], {
type: 'application/pdf',
encoding: 'UTF-8'
})
);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
link.remove();
}
});
I'm trying to generate a xlsx file using SheetJS in node. Then in my frontend I have a button to call this route and download the file. Currently I have the following:
// backend code
export function exportExcel(req: Request, res: Response) {
try {
const workbook = utils.book_new();
const fileName = "sample";
const dataSheet = utils.json_to_sheet(sampleData);
utils.book_append_sheet(workbook, dataSheet, fileName.replace("/", ""));
const binaryWorkbook = write(workbook, {
type: "array",
bookType: "xlsx",
});
return res.status(OK).send(binaryWorkbook);
} catch (_error) {
return res.sendStatus(500)
}
}
Then in the front end I have the following:
const handleExcelExport = () => {
const { data } = await axios.get(
`/export-excel`,
{
responseType: "blob",
}
).then(response => {
const blob = new Blob([response], {
type: "application/octet-stream",
});
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();
}
}
// I simply call the above in the onClick handler in a button
<button onClick={handleExcelExport}> Export excel </button>
I can see that a download file appears when I click on the button but I can't open it. MS Excel says that ""File format or file extension is not valid..."
I fixed by changing to the following in the BE:
const binaryWorkbook = write(workbook, {
type: "array",
bookType: "xlsx",
});
and also adding headers per the docs:
res.setHeader(
"Content-Disposition",
'attachment; filename="SheetJSNode.xlsx"'
);
res.setHeader("Content-Type", "application/vnd.ms-excel");
Then in the frontend I changed the response type and content-type to
{
responseType: "arraybuffer",
}
Blob content-type
const blob = new Blob([response], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
I'm trying to make an API request using HttpClient to download a CSV file. I had to switch from Axios to HttpClient due to ForgeRock authentication that requires a bearer token. I'm having an issue where the response coming back does not have response.data. Where would I find the data to download to CSV?
My code:
fetchCSV = date => {
const url = "https://<URLdownloadLink>?runDate="+date;
const method = "GET";
HttpClient
.request({
url,
method,
bypassAuthentication: false,
responseType: 'blob',
init: {},
timeout: 5000
})
.then((response) => {
console.log("data", response)
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'Asset Manager.csv');
document.body.appendChild(link);
link.click();
link.remove();
})
.catch(error => {
console.log("error", error);
})
}
The response I'm getting back is the below. The response does not have "response.data" as an object key. When I open the CSV it only has "undefined".
This is my response.body:
You are using response.data instead of response.body?
Thats why it says undefined.
Remember CSV is just plain text and response.data is undefined.
fetchCSV = date => {
const url = "https://<URLdownloadLink>?runDate=" + date;
const method = "GET";
HttpClient
.request({
url,
method,
bypassAuthentication: false,
responseType: 'blob',
init: {},
timeout: 5000
})
.then((response) => {
console.log("data", response)
const url = window.URL.createObjectURL(new Blob([response.body]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'Asset Manager.csv');
document.body.appendChild(link);
link.click();
link.remove();
})
.catch(error => {
console.log("error", error);
})
}
I am new to Nodejs, so finding difficult to achieve the above-mentioned task. Trying to call an async function and return its value in response for a POST method
module.exports.pdf = async(event, context, callBack) => {
const data = {
title: " Pdf generation using puppeteer",
text: " Handlebar is awesome!"
}
const executablePath = event.isOffline ?
"./node_modules/puppeteer/.local-chromium/mac-674921/chrome-mac/Chromium.app/Contents/MacOS/Chromium" :
await chromium.executablePath;
const file = fs.readFileSync(path.resolve(__dirname, "template.hbs"), 'utf8')
const template = handlebars.compile(file)
const html = template(data)
let browser = null;
try {
browser = await chromium.puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath,
headless: chromium.headless
});
const page = await browser.newPage();
page.setContent(html);
const pdf = await page.screenshot({ encoding: "base64" })
// const pdf = await page.pdf({
// format: "A4",
// printBackground: true,
// margin: { top: "1cm", right: "1cm", bottom: "1cm", left: "1cm" }
// });
// TODO: Response with PDF (or error if something went wrong )
const response = {
headers: {
"Content-type": "application/json",
"content-disposition": "attachment; filename=test.pdf"
},
statusCode: 200,
body: pdf.toString("base64"),
isBase64Encoded: true
};
const output_filename = 'pdf-demo.json';
const s3Params = {
Bucket: "pdf-demo-screenshot",
Key: `public/pdfs/${output_filename}`,
Body: pdf,
ContentType: "application/json",
ServerSideEncryption: "AES256"
};
s3.putObject(s3Params, err => {
if (err) {
console.log("err", err);
return callBack(null, { error });
}
});
context.succeed(response);
} catch (error) {
return context.fail(error);
} finally {
if (browser !== null) {
await browser.close();
}
}
};
I have the above function. I have a global variable let img_arr = []. And the below POST method where I want to call the above function and push the base64 data into the array and return the array in the response.
app.use('/screenshot', function(req, res) {
res.send(img_arr)})
I have a page with a button. When I click that button, it opens a PDF in a new tab.
How can I download the PDF as a file with puppeteer?
Maybe I can write a file with the buffer from the new tab. But I am not sure how.
A simple solution is to use the fetch api to execute the GET request. This way you can read the response, pass it to your backend and save it to disk.
Use this sample code as reference:
import fs from 'fs';
async function downloadImage(page: any, url: string, fullpath: string) {
const data = await page.evaluate(
// tslint:disable-next-line no-shadowed-variable
async ({ url }) => {
function readAsBinaryStringAsync(blob) {
return new Promise((resolve, reject) => {
const fr = new FileReader();
fr.readAsBinaryString(blob);
fr.onload = () => {
resolve(fr.result);
};
});
}
const r = await fetch(url, {
credentials: 'include',
headers: {
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp, */*;q=0.8',
'cache-control': 'no-cache',
pragma: 'no-cache',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'same-site',
'upgrade-insecure-requests': '1'
},
referrerPolicy: 'no-referrer-when-downgrade',
body: null,
method: 'GET',
mode: 'cors'
});
return await readAsBinaryStringAsync(await r.blob());
},
{ url }
);
fs.writeFileSync(fullpath, data, { encoding: 'binary' });
}
Use puppeteer-extra node module.
Puppeteer-extra
const puppeteer = require('puppeteer-extra');
...
...
puppeteer.use(require('puppeteer-extra-plugin-user-preferences')({userPrefs: {
download: {
prompt_for_download: false,
open_pdf_in_system_reader: true
},
plugins: {
always_open_pdf_externally: true // this should do the trick
}
}}))
const browser = await puppeteer.launch();
browser.on('targetcreated', async (target) => {
console.log('targetcreated');
if (target.type() !== 'page') {
return;
}
try {
const pageList = await browser.pages();
pageList.forEach((page) => {
page._client.send('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: './pdfDownloaded/',
});
});
} catch (e) {
console.log("targetcreated", e);
}
});
...
...
But when i set the always_open_pdf_externally: true chrome crashes.
try if it works for you, and please post back the answer if you found any