How to build PDF file from binary string returned from a web-service using javascript
Solution 1
Is there any solution like building a pdf file on file system in order to let the user download it?
Try setting responseType
of XMLHttpRequest
to blob
, substituting download
attribute at a
element for window.open
to allow download of response from XMLHttpRequest
as .pdf
file
var request = new XMLHttpRequest();
request.open("GET", "/path/to/pdf", true);
request.responseType = "blob";
request.onload = function (e) {
if (this.status === 200) {
// `blob` response
console.log(this.response);
// create `objectURL` of `this.response` : `.pdf` as `Blob`
var file = window.URL.createObjectURL(this.response);
var a = document.createElement("a");
a.href = file;
a.download = this.response.name || "detailPDF";
document.body.appendChild(a);
a.click();
// remove `a` following `Save As` dialog,
// `window` regains `focus`
window.onfocus = function () {
document.body.removeChild(a)
}
};
};
request.send();
Solution 2
I realize this is a rather old question, but here's the solution I came up with today:
doSomethingToRequestData().then(function(downloadedFile) {
// create a download anchor tag
var downloadLink = document.createElement('a');
downloadLink.target = '_blank';
downloadLink.download = 'name_to_give_saved_file.pdf';
// convert downloaded data to a Blob
var blob = new Blob([downloadedFile.data], { type: 'application/pdf' });
// create an object URL from the Blob
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
// set object URL as the anchor's href
downloadLink.href = downloadUrl;
// append the anchor to document body
document.body.append(downloadLink);
// fire a click event on the anchor
downloadLink.click();
// cleanup: remove element and revoke object URL
document.body.removeChild(downloadLink);
URL.revokeObjectURL(downloadUrl);
}
Solution 3
I changed this:
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf,'
+ escape(pdfText)
+ '"></embed>';
to
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf;base64,'
+ escape(pdfText)
+ '"></embed>';
and it worked for me.
Solution 4
The answer of @alexandre with base64 does the trick.
The explanation why that works for IE is here
https://en.m.wikipedia.org/wiki/Data_URI_scheme
Under header 'format' where it says
Some browsers (Chrome, Opera, Safari, Firefox) accept a non-standard ordering if both ;base64 and ;charset are supplied, while Internet Explorer requires that the charset's specification must precede the base64 token.
Solution 5
Detect the browser and use Data-URI for Chrome and use PDF.js as below for other browsers.
PDFJS.getDocument(url_of_pdf)
.then(function(pdf) {
return pdf.getPage(1);
})
.then(function(page) {
// get a viewport
var scale = 1.5;
var viewport = page.getViewport(scale);
// get or create a canvas
var canvas = ...;
canvas.width = viewport.width;
canvas.height = viewport.height;
// render a page
page.render({
canvasContext: canvas.getContext('2d'),
viewport: viewport
});
})
.catch(function(err) {
// deal with errors here!
});
Admin
Updated on July 09, 2022Comments
-
Admin almost 2 years
I am trying to build a PDF file out of a binary stream which I receive as a response from an Ajax request.
Via
XmlHttpRequest
I receive the following data:%PDF-1.4.... ..... ....hole data representing the file .... %% EOF
What I tried so far was to embed my data via
data:uri
. Now, there's nothing wrong with it and it works fine. Unfortunately, it does not work in IE9 and Firefox. A possible reason may be that FF and IE9 have their problems with this usage of thedata-uri
.Now, I'm looking for any solution that works for all browsers. Here's my code:
// responseText encoding pdfText = $.base64.decode($.trim(pdfText)); // Now pdfText contains %PDF-1.4 ...... data...... %%EOF var winlogicalname = "detailPDF"; var winparams = 'dependent=yes,locationbar=no,scrollbars=yes,menubar=yes,'+ 'resizable,screenX=50,screenY=50,width=850,height=1050'; var htmlText = '<embed width=100% height=100%' + ' type="application/pdf"' + ' src="data:application/pdf,' + escape(pdfText) + '"></embed>'; // Open PDF in new browser window var detailWindow = window.open ("", winlogicalname, winparams); detailWindow.document.write(htmlText); detailWindow.document.close();
As I have said, it works fine with Opera and Chrome (Safari hasn't been tested). Using IE or FF will bring up a blank new window.
Is there any solution like building a PDF file on a file system in order to let the user download it? I need the solution that works in all browsers, at least in IE, FF, Opera, Chrome and Safari.
I have no permission to edit the web-service implementation. So it had to be a solution at client-side. Any ideas?
-
isobretatel about 11 yearsWorked in Android browser?
-
Juanjo over 8 years-1. Interesting but is not what the user asked. He asked specifically from a web service to Javascript to do it in the client side, Do not assume the user even controls the web service or that he is using a server side language.
-
zezollo over 7 yearsWorks fine for me too, except the final
removeChild(a)
, what returned an error (something like "Node not found"). So I just didn't triggered this removal onwindow.onfocus
and instead directly putdocument.body.removeChild(a)
right aftera.click()
. -
guest271314 over 7 years@zezollo
window
gaining focus beforeclick
event? Could probably be replaced withdocument.body.appendChild(a);a.onclick = function() { window.onfocus = function () { document.body.removeChild(a); window.onfocus = null; } } a.click()
-
zezollo over 7 yearsNo, the error shows up after closing the pdf file viewer (software outside browser, not pdf.js). I can't understand this error, because when inspecting the html code, it does include the
a
element at the right place. Anyway, is this a problem to remove the child right after theclick
? This works really fine. This is used here too: stackoverflow.com/a/18197341/3926735 -
guest271314 over 7 yearsNo, not an issue to remove element following calling
.click()
on element -
Endless over 7 yearsDo you need to do it with ajax? You can simply put the url as href and download it directly (but only if it's a simple get request)
-
Chris Cashwell over 7 yearsUsing a simple anchor is not an option in our case, AJAX was necessary.
-
manuel-84 over 7 yearsto me the latest chrome on windows seems ignoring the a.click(), and the same for firefox on linux
-
guest271314 over 7 years@manuel-84 Are popup blockers enabled at the browsers? See also How to download a file without using <a> element with download attribute or a server?
-
Pacerier over 6 years@Alexandre, This solution doesn't scale.
-
Jyoti Duhan over 6 yearswill it work if the response type at api side is application/json or application/binary??
-
guest271314 over 6 years@JyotiDuhan Why would a
.pdf
be served with anapplication/json
content type? -
Jyoti Duhan over 6 yearsYes, got it now. It should be binary. Thanks :)
-
AKJ almost 6 yearsAdding
responseType
toblob
worked for me in AngularJS. -
Allen over 4 yearsIf you run into issues with IE complaining about the responseType, check out this answer for the fix.
-
Urasquirrel about 4 yearsThis doesn't seem to work consistently in React and other virtualized dom libraries where a library maintains your dom, and you shouldn't be directly interacting with it in this way.
-
Chris Cashwell about 4 years@Urasquirrel it's also not 2016 anymore. Time changes all things.