pdf file not opening in browser - javascript

I am receiving a pdf file as api call response from a node.js backend.The file opens in browser window in an encoded format.I have tried to download but the downloaded file has error opening it (error: failed to load pdf document).I am told the response body is base64 encoded.
Is their any way the pdf can be open /downloaded correctly.I am using react.js and is new to it.
code snippet :
import FileDownload from 'js-file-download';
export function getTaxInvoice({token}){
const authString = `Bearer ${token}`;
return (dispatch) => {
return axios.get(`${MAIN_URL}/rental_invoice`,{
headers: {Authorization: authString, 'Accept': 'application/json','Content-Type': 'application/pdf'},
responseType: "arraybuffer",//I have tried with blob as well
encoding: null
})
.then((response)=>{
FileDownload(response, 'some.pdf');
const taxInvoiceUrl = window.URL.createObjectURL(new Blob([response.data]));
window.open(taxInvoiceUrl, "_blank");
console.log( response);
// dispatch(taxInvoiceLoadSuccess(taxInvoiceUrl));
// dispatch(onViewChanged("rental_invoice"));
})
.catch((error)=>{
dispatch(taxInvoiceLoadFailed());
})
}
}
response from api call:image snippet

Here is an example of some code I have used in the past to do this:
function downloadURI (url, name) {
var link = document.createElement('a')
link.download = name
link.href = url
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
export function download (url, type = 'application/pdf', name = 'example') {
get(url, (err, result) => {
if (err) return handleError(err)
const blob = new Blob([result.body], { type })
const downloadUrl = URL.createObjectURL(blob)
downloadURI(downloadUrl, name)
})
}
It will download the file and create an object url and automatically trigger opening the file by programatically clicking a link.

Finally solved the issue.(My senior dev helped me).Final code is below:
install base64js and filedownload on npm .
export function getTaxInvoice({token}){
const authString = `Bearer ${token}`;
return (dispatch) => {
return axios.get(`${MAIN_URL}/rental_invoice`,{
headers: {Authorization: authString, 'Accept': 'application/pdf','Content-Type': 'application/pdf'}
})
.then((response)=>{
FileDownload(base64js.toByteArray(response.data), 'some.pdf');
const taxInvoiceUrl = window.URL.createObjectURL(new Blob([base64js.toByteArray(response.data)], { type: "application/pdf" }) );
window.open(taxInvoiceUrl, "_blank");
dispatch(taxInvoiceLoadSuccess(response.data));
dispatch(onViewChanged("rental_invoice"));
})
.catch((error)=>{
console.log(error);
dispatch(taxInvoiceLoadFailed());
})
}
}

Try changing
FileDownload(response, 'some.pdf');
to
FileDownload(response.data, 'some.pdf');

Related

Unable to select text in pdf when downloading from NGINX server

I have the following code in a NodeJS app using the library node-html-pdf:
pdf.create(PDF_html).toStream((err, pdfStream) => {
if (err) {
console.log(err)
return res.sendStatus(500)
} else {
res.statusCode = 200
res.setHeader('Content-type', 'application/pdf')
res.attachment()
pdfStream.on('end', () => {
return res.end()
})
pdfStream.pipe(res)
}
})
On the client side I am using fetch to retrieve and download the PDF using the following code:
document.getElementById('pdf_button').addEventListener("click", function() {
let query_nr = ''
let query_spnr = ''
let url = 'https://{my.public.server.url}/getPDF/'
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
"nr": query_nr,
"spnr": query_spnr
})
})
.then((response) => response.blob())
.then((blob) => {
blob = new Blob([blob], {type: "application/pdf"});
const objectURL = URL.createObjectURL(blob);
const a = document.createElement("a")
document.body.appendChild(a)
a.style = "display: none"
a.href = objectURL
a.type = 'application/pdf'
a.download = query_nr + '-' + query_spnr + '.pdf'
console.log(a);
a.click()
// window.open(objectURL, '_blank');
})
.catch((error) => {
console.error(error);
})
});
The above code works fine as long I am using localhost as url, and I am able to select text from the downloaded text.
When I download the PDF from the server it seems like the whole PDF-file gets converted to an image (I might be very wrong), and I am not able to select any text.
Does anyone know how I can fix this? And why is this happening?

How to download a ReadableStream on the browser that has been returned from fetch

I am receiving a ReadableStream from a server, returned from my fetch call.
A ReadableStream is returned but I don't know how to trigger a download from this stage. I can't use the url in an href because it requires an Authorization token.
I don't want to install fs on the client so what options do I have?
try {
const res = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/octet-stream'
}
});
const blob = await res.blob();
const newBlob = new Blob([blob]);
const newUrl = window.URL.createObjectURL(newBlob);
const link = document.createElement('a');
link.href = newUrl;
link.setAttribute('download', 'filename');
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
window.URL.revokeObjectURL(newBlob);
} catch (error) {
console.log(error);
}
Update 1
I converted the file to a Blob, then passed it into a newly generated href. Successfully downloaded a file. The end result was the ReadStream contents as a .txt file.
Meaning stuff like this
x:ÚêÒÓ%¶âÜTb∞\܃
I have found 2 solutions, both worked but I was missing a simple addition to make them work.
The native solution is
try {
const res = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`
}
});
const blob = await res.blob();
const newBlob = new Blob([blob]);
const blobUrl = window.URL.createObjectURL(newBlob);
const link = document.createElement('a');
link.href = blobUrl;
link.setAttribute('download', `${filename}.${extension}`);
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
// clean up Url
window.URL.revokeObjectURL(blobUrl);
This version is using the npm package steamSaver for anyone who would prefer it.
try {
const res = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`
}
});
const fileStream = streamSaver.createWriteStream(`${filename}.${extension}`);
const writer = fileStream.getWriter();
const reader = res.body.getReader();
const pump = () => reader.read()
.then(({ value, done }) => {
if (done) writer.close();
else {
writer.write(value);
return writer.ready.then(pump);
}
});
await pump()
.then(() => console.log('Closed the stream, Done writing'))
.catch(err => console.log(err));
The key for why it was not working was because I did not include the extension, so it either errored out because of the mimetype was wrong or it opens a .txt file with a string of the body instead of the image.

PDF is blank and damaged when downloading it from API using JavaScript and React JS

I am downloading a pdf file from API, but I am getting a blank PDF. I have tested the API endpoint and able to get the byte stream on the console and when I save it to File, it got saved and the file looks good. Getting the same response back to the front end using React and I could see the PDF byte stream in the response.
However, I could not see the content. It says the file is damaged or corrupted when I opened the downloaded PDF from my local.
I have looked at many examples and are following the same pattern, but I think I am missing something here.
My API Java endpoint definition looks like below
#GetMapping(value = "/fetchFile")
public ResponseEntity<byte[]> fetchFile(#RequestParam final String key) {
FileResponse response = myService.readFile(key);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + key.substring(key.lastIndexOf('/') + 1) + "\"");
return Mono.just(ResponseEntity.ok().headers(httpHeaders).contentLength(response.getContentLength())
.contentType(MediaType.parseMediaType(response.getContentType()))
.body(response.getResponseBytes()));
}
Frontend:
rounterFetchFile.js
router.get('/', (request, resp) => {
axios({
method: 'get',
baseURL: 'http://mybackend.apibase.url',
responseType: 'blob',
url: '/fetchFile',
params: {
fileKey: 'myfile.pdf'
}
})
.then(response => {
return resp.send(response.data)
})
.catch(error => {
console.error(error)
return resp.status(error.response.status).end()
})
})
in myFileComoponent.js
//a function that reads the response from rounterFetchFile.js
const getDocumentOnClick = async () => {
try {
var {data} = await pullMyPDF()
var blob = new Blob([data], { type: "application/pdf" });
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "myFileName.pdf";
link.click();
} catch (e) {
console.log(e)
}
}
Here
var {data} = await pullMyPDF()
is returning the following content. I compared it with the result returned by the Postman, and it is the same. The generated file size is not empty from the react too. I am not able to find out where is it wrong
Below is the response from API endpoint for the fetchFile
I had a similar problem and I fixed it with this:
spa
axios.post(
'api-url',
formData,
{
responseType: 'blob',
headers: {
'Accept': 'application/pdf'
}
})
.then( response => {
const url = URL.createObjectURL(response.data);
this.setState({
filePath: url,
fileType: 'pdf',
})
})
.catch(function (error) {
console.log(error);
});
api
[HttpPost]
public async Task<IActionResult> Post()
{
var request = HttpContext.Request;
var pdfByteArray = await convertToPdfService.ConvertWordStreamToPdfByteArray(request.Form.Files[0], "application/msword");
return File(pdfByteArray, "application/pdf");
}
When the response type is a blob and accepted 'application / pdf' in the header, with that config the job is done ;) ...
Something that worked for me was to send the bytes as base64 from the controller.
API:
public async Task<ActionResult> GetAsync() {
var documentBytes = await GetDocumentAsync().ConfigureAwait(false);
return Ok(Convert.ToBase64String(documentBytes))
}
Front End:
client.get(url, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => {
const link = document.createElement('a');
link.href = "data:application/octet-stream;base64," + response.data;
link.download = 'file.pdf';
link.click();
})
.catch(error => {
console.log(error);
})
I hope this solves your problem.

I have a problem downloading xlsx file from API rest on JS

Im working on ReactJs. I have a button that downloads a excel file that is provided from an API REST.The problem is that i read all the content from the response and then i download all this information on a string.
But i have been trying all different stuff and i allways have the same problem, when i try to open the file in Excel,it show a message saying that the file is corrupted.
This is my code:
export const getFilesFromApi = (path, callBack, contentType) => apiFileCalls({ path, method: 'GET', callBack, contentType: contentType || 'text/plain' });
export const apiFileCalls = ({ path, callBack, data, method, contentType, attachFiles = [], additionalHeaders = [] }) => {
debugger;
new Promise(() => {
const request = superagent(method, path);
request.set('Access-Control-Allow-Origin', '*');
if (contentType) {
request.set('Content-Type', contentType);
}
const accessToken = getCookie_accessToken();
if (accessToken) {
request.set('Authorization', `Bearer ${accessToken}`);
}
if (data) {
request.send(data);
}
additionalHeaders.forEach((header) => request.set(header.name, header.value));
attachFiles.forEach((file) => request.attach(file.fieldName, file.file));
request.query().then((res) => callBack(res.text)).catch((error) => {
if (error.status !== 401) {
callBack(true, error.response);
}
});
});
};
export const downloadFile = (content, fileName) => {
debugger;
const urlrtMP = window.URL.createObjectURL(new Blob([ content ]));
const link = document.createElement('a');
link.href = urlrtMP;
link.setAttribute('download', fileName);
link.click();
};
Any help? Thanks a lot
You have to use responseType('blob') for receiving Blob content.
Ref: https://visionmedia.github.io/superagent/#binary

Download PDF from http response with Axios

I am working on a Vue application with a Laravel back-end API. After clicking on a link I would like to do a call to the server to download a certain file (most of the time a PDF file). When I do a get request with axios I get a PDF in return, in the body of the response. I would like to download that file directly.
To give you a better view of how the response is looking like:
(note: I know a real text response is better than an image but I don't see any way to return that because of the length of the actual PDF content..)
Is there any way of downloading that file with JavaScript or something? It has to be specific a direct download without clicking on the button again.
Code
// This method gets called when clicking on a link
downloadFile(id) {
const specificationId = this.$route.params.specificationId;
axios
.get(`${this.$API_URL}/api/v1/suppliersmanagement/product-specifications/${specificationId}/fileupload/${id}/download`, {
headers: this.headers,
})
.then(response => {
console.log(response);
// Direct download the file here..
})
.catch(error => console.log(error));
},
As #Sandip Nirmal suggested I've used downloadjs and that worked out pretty good! Had to make a few adjustments to my code but in the end it worked out.
My new code
// npm i downloadjs
import download from 'downloadjs'
// method
downloadFile(file) {
const specificationId = this.$route.params.specificationId;
axios
.get(`${this.$API_URL}/api/v1/suppliersmanagement/product-specifications/${specificationId}/fileupload/${file.id}/download`, {
headers: this.headers,
responseType: 'blob', // had to add this one here
})
.then(response => {
const content = response.headers['content-type'];
download(response.data, file.file_name, content)
})
.catch(error => console.log(error));
},
You should use 'responseType' option. For example:
axios.get(
url,
{responseType: 'blob'} // !!!
).then((response) => {
window.open(URL.createObjectURL(response.data));
})
You have 2 options for this. If you want to do it from server and if you are using Node.js as a backend. You can do it easily using res.download method of express. You can follow this answer for that Download a file from NodeJS Server using Express.
But if you want to handle it from client then there are few options since you can't use axios, XHR, fetch to download file directly. You can either use download.js or write your own code in following way.
return axios({
url: '/download', // download url
method: 'get',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
mode: 'no-cors'
}
})
.then(response => response.blob())
.then(blob => {
var url = window.URL.createObjectURL(blob)
var a = document.createElement('a')
a.href = url
a.download = fileName
a.click()
a.remove()
setTimeout(() => window.URL.revokeObjectURL(url), 100)
})
Since response returned from server is in json format you need to convert it into ObjectURL and set it to anchor tag.
If you sneak inside download.js code you will find same implementation.
2022 answer: using node.js, fs.promises and async/await
The key is using responseType: 'stream' per the Axios docs.
import axios from 'axios';
import { writeFile } from 'fs/promises';
const downloadFile = async () => {
const response = await axios.get('https://someurl', {
params: {
// ...
},
// See https://axios-http.com/docs/api_intro
responseType: 'stream',
});
const pdfContents = response.data;
await writeFile('file.pdf', pdfContents);
};
You can do it like this
download(filename) {
fetch(url , { headers })
.then(response => response.blob())
.then(blob => URL.createObjectURL(blob))
.then(uril => {
var link = document.createElement("a");
link.href = uril;
link.download = filename + ".csv";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
}
here I want to download a CSV file, So I add .csv to the filename.
const downloadPDF = (id, fileName) => {
axios({
method: 'get',
url: `https://api.example.com/pdf/invoice/${id}`,
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
'Content-Type': 'application/json'
},
responseType: 'blob'
}).then(function (response) {
const a = document.createElement('a');
a.href = window.URL.createObjectURL(response.data);
a.download = `${fileName}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
});
}

Categories