97

I want to force the browser to download a pdf file.

I am using the following code :

<a href="../doc/quot.pdf" target=_blank>Click here to Download quotation</a>

It makes the browser open the pdf in a new window, but I want it to download to the hard drive when a user clicks it.

I found that Content-disposition is used for this, but how do I use it in my case?

Pranav 웃
  • 8,469
  • 6
  • 38
  • 48
Krish
  • 2,590
  • 8
  • 42
  • 62
  • 4
    possible duplicate of [How to implement Content-Disposition: attachment?](http://stackoverflow.com/questions/8875949/how-to-implement-content-disposition-attachment) – Quentin Feb 08 '12 at 14:37

2 Answers2

149

On the HTTP Response where you are returning the PDF file, ensure the content disposition header looks like:

Content-Disposition: attachment; filename=quot.pdf;

See content-disposition on the wikipedia MIME page.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • 2
    @Oded : And in case the file is opened locally *(or any other cases without an http server)*? – user2284570 Sep 06 '14 at 19:30
  • @user2284570 - that would depend on specific browser and OS settings. – Oded Sep 06 '14 at 19:49
  • 1
    @user2284570 - don't know. Looks easy enough to test locally, no? – Oded Sep 06 '14 at 21:28
  • @Oded : did you looked to my last comment on the linked answer? The file isn't downloaded as HTML. There's also http://www.w3schools.com/tags/att_a_download.asp, but I don't know it's JavaScript equivalent. – user2284570 Sep 06 '14 at 21:44
  • Very usefull for me, thanks, i spent hours to find why Chrome doesn't display pdf file into his viewer, the problem was that I return file in Respose like an attachment. So in my case for download a pdf in Response I used Content-Disposition: attachment; and for display a pdf i used Content-Disposition: inline;. Thank you very much for you answer. – Alexei Bondarev Apr 14 '16 at 10:53
  • Be careful: If your filename contains spaces, the latter segment will be truncated. Enclose the filename in quotes to avoid this. Quotes that happen to exist in the filename can be escaped by a backslash. – Milos Ivanovic Oct 17 '16 at 22:06
  • to force browser to download automatically, you will also need to set the content-type : application/octet-stream; along with the content disposition – Gurpreet May 08 '22 at 20:23
21

With recent browsers you can use the HTML5 download attribute as well:

<a download="quot.pdf" href="../doc/quot.pdf">Click here to Download quotation</a>

It is supported by most of the recent browsers except MSIE11. You can use a polyfill, something like this (note that this is for data uri only, but it is a good start):

(function (){

    addEvent(window, "load", function (){
        if (isInternetExplorer())
            polyfillDataUriDownload();
    });

    function polyfillDataUriDownload(){
        var links = document.querySelectorAll('a[download], area[download]');
        for (var index = 0, length = links.length; index<length; ++index) {
            (function (link){
                var dataUri = link.getAttribute("href");
                var fileName = link.getAttribute("download");
                if (dataUri.slice(0,5) != "data:")
                    throw new Error("The XHR part is not implemented here.");
                addEvent(link, "click", function (event){
                    cancelEvent(event);
                    try {
                        var dataBlob = dataUriToBlob(dataUri);
                        forceBlobDownload(dataBlob, fileName);
                    } catch (e) {
                        alert(e)
                    }
                });
            })(links[index]);
        }
    }

    function forceBlobDownload(dataBlob, fileName){
        window.navigator.msSaveBlob(dataBlob, fileName);
    }

    function dataUriToBlob(dataUri) {
        if  (!(/base64/).test(dataUri))
            throw new Error("Supports only base64 encoding.");
        var parts = dataUri.split(/[:;,]/),
            type = parts[1],
            binData = atob(parts.pop()),
            mx = binData.length,
            uiArr = new Uint8Array(mx);
        for(var i = 0; i<mx; ++i)
            uiArr[i] = binData.charCodeAt(i);
        return new Blob([uiArr], {type: type});
    }

    function addEvent(subject, type, listener){
        if (window.addEventListener)
            subject.addEventListener(type, listener, false);
        else if (window.attachEvent)
            subject.attachEvent("on" + type, listener);
    }

    function cancelEvent(event){
        if (event.preventDefault)
            event.preventDefault();
        else
            event.returnValue = false;
    }

    function isInternetExplorer(){
        return /*@cc_on!@*/false || !!document.documentMode;
    }
    
})();
inf3rno
  • 24,976
  • 11
  • 115
  • 197
  • 3
    It looks like it has fairly good browser support except IE 11 and mobile safari: http://caniuse.com/#feat=download – Stephen Ostermiller Apr 20 '17 at 08:46
  • 1
    @StephenOstermiller Recent browsers support ES7 async/await and ES6 classes as well (except MSIE ofc.) Public sites can be optimized for MSIE while admin sites can be optimized for other browsers with new features. Or you can use an MSIE polyfill. – inf3rno Apr 20 '17 at 14:42
  • 2
    Widely supported now but note a same-origin restriction applies in most (all?) browsers. – rymo Jul 14 '20 at 20:23
  • 1
    Really useful the download attribute, it simplified a lot what I wanted to do, +1 – Jesús Hagiwara Dec 30 '22 at 15:23