¿Cómo utilizar la disposición de contenido para forzar la descarga de un archivo en el disco duro?

76

Quiero forzar al navegador a descargar un pdfarchivo.

Estoy usando el siguiente código :

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

Hace que el navegador abra el pdf en una nueva ventana, pero quiero que se descargue en el disco duro cuando un usuario hace clic en él.

Descubrí que Content-dispositionse usa para esto, pero ¿cómo lo uso en mi caso?

Krish
fuente

Respuestas:

124

En la respuesta HTTP donde está devolviendo el archivo PDF, asegúrese de que el encabezado de disposición de contenido tenga este aspecto:

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

Ver disposición de contenido en la página MIME de wikipedia.

Oded
fuente
2
@Oded: ¿Y en caso de que el archivo se abra localmente (o en cualquier otro caso sin un servidor http) ?
user2284570
@ user2284570: eso dependería de la configuración específica del navegador y del sistema operativo.
Oded
1
@ user2284570 - no lo sé. Parece bastante fácil de probar localmente, ¿no?
Oded
@Oded: ¿miraste mi último comentario sobre la respuesta vinculada? El archivo no se descarga como HTML. También está w3schools.com/tags/att_a_download.asp , pero no sé si es equivalente a JavaScript.
user2284570
Muy útil para mí, gracias, pasé horas para descubrir por qué Chrome no muestra el archivo pdf en su visor, el problema es que devuelvo el archivo en Respose como un archivo adjunto. Entonces, en mi caso, para descargar un pdf en Response, usé Content-Disposition: adjunto; y para mostrar un pdf utilicé Content-Disposition: inline ;. Muchas gracias por tu respuesta.
Alexei Bondarev
14

Con los navegadores recientes, también puede usar el atributo de descarga HTML5:

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

Es compatible con la mayoría de los navegadores recientes, excepto MSIE11. Puede usar un polyfill, algo como esto (tenga en cuenta que esto es solo para datos uri, pero es un buen comienzo):

(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
fuente
3
Parece que tiene un soporte de navegador bastante bueno, excepto IE 11 y safari móvil: caniuse.com/#feat=download
Stephen Ostermiller
1
@StephenOstermiller Los navegadores recientes también admiten las clases ES7 async / await y ES6 (excepto MSIE ofc.) Los sitios públicos se pueden optimizar para MSIE, mientras que los sitios de administración se pueden optimizar para otros navegadores con nuevas funciones. O puede utilizar un polyfill MSIE.
inf3rno
1
Ampliamente compatible ahora, pero tenga en cuenta que se aplica una restricción del mismo origen en la mayoría de los navegadores (¿todos?).
Rymo