Como tornar o arquivo PDF para download no link HTML?

124

Estou fornecendo o link de um arquivo pdf na minha página da web para download, como abaixo

<a href="myfile.pdf">Download Brochure</a>

O problema é quando o usuário clica neste link e, em seguida,

  • Se o usuário instalou o Adobe Acrobat, ele abrirá o arquivo na mesma janela do navegador no Adobe Reader.
  • Se o Adobe Acrobat não estiver instalado, ele será exibido para o usuário em Download do arquivo.

Mas eu quero que ele sempre apareça para o usuário fazer o download, independentemente do "Adobe acrobat" estar instalado ou não.

Por favor me diga como eu posso fazer isso?

Prashant
fonte

Respostas:

116

Em vez de vincular ao arquivo .PDF, faça algo como

<a href="pdf_server.php?file=pdffilename">Download my eBook</a>

que gera um cabeçalho personalizado, abre o PDF (cofre binário) e imprime os dados no navegador do usuário; eles podem optar por salvar o PDF, apesar das configurações do navegador. O pdf_server.php deve ficar assim:

header("Content-Type: application/octet-stream");

$file = $_GET["file"] .".pdf";
header("Content-Disposition: attachment; filename=" . urlencode($file));   
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");            
header("Content-Length: " . filesize($file));
flush(); // this doesn't really matter.
$fp = fopen($file, "r");
while (!feof($fp))
{
    echo fread($fp, 65536);
    flush(); // this is essential for large downloads
} 
fclose($fp); 

PS: e, obviamente, execute algumas verificações de integridade na variável "file" para impedir que as pessoas roubem seus arquivos, como não aceitam extensões de arquivo, negam barras, adicione .pdf ao valor

TravisO
fonte
2
Estou enfrentando outro problema com isso: meu arquivo está localizado em /products/brochure/myfile.pdf Estou dando a variável $ file como $ file_path = $ _SERVER ['DOCUMENT_ROOT']. '/ Products / brochure /'. arquivo $; mas está baixando o arquivo como "% 2Fvar% 2Fwww% 2Fweb15% 2Fweb% 2Fproducts% 2Fbrochure% 2myfile.pdf"
Prashant
3
O @TravisO "Content-type: application/force-download"não está listado em nenhum lugar aqui: iana.org/assignments/media-types/application É um cabeçalho completamente falso. Por favor, não crie cabeçalhos e envie-os. Você pode atualizar sua resposta. Obrigado.
26513 Nicholas Nicholas
4
Tenha cuidado ao usar esse código literalmente. Isso introduz uma grave vulnerabilidade de LFI, à medida que você passa variáveis ​​GET diretamente fopen.
Joost
4
Esse código é provavelmente perigoso de outra maneira. Se você passar links HTTP para fopen, acho que ele recuperará o arquivo de outro servidor. Pode ser usado por um invasor para tentar verificar sua rede interna em busca de arquivos PDF expostos.
Rory McCune 4/15
2
Sem mencionar como seria fácil ignorar quaisquer "verificações de sanidade" que você pensa que fará no parâmetro "file". Ataques de injeção de caminho / travessia de diretório são extremamente prováveis.
AviD 04/09/2015
209

Esse é um problema comum, mas poucas pessoas sabem que existe uma solução simples em HTML 5:

<a href="./directory/yourfile.pdf" download="newfilename">Download the pdf</a>

Onde newfilenameestá o nome do arquivo sugerido para o usuário salvar o arquivo. Ou o padrão será o nome do arquivo no lado do servidor, se você o deixar vazio, assim:

<a href="./directory/yourfile.pdf" download>Download the pdf</a>

Compatibilidade: testei isso no Firefox 21 e no Iron, ambos funcionaram bem. Pode não funcionar em navegadores incompatíveis com HTML5 ou desatualizados. O único navegador que testei que não forçou o download é o IE ...

Verifique a compatibilidade aqui: http://caniuse.com/#feat=download

T_D
fonte
9
Esta é uma solução simples, mas infelizmente não é amplamente suportada, esp. nenhum suporte do IE caniuse.com/#feat=download
benebun 13/11/2013
2
Sim, eu sei direito. É por isso que tenho uma nota lateral sobre compatibilidade. E, de acordo com a sua fonte, o IE e o Safari não suportam essa abordagem, ou pelo menos ainda não :) De qualquer forma, se você quiser que todos os navegadores force o download, sugiro verificar algumas das outras respostas ...
T_D
3
funciona como um encanto com o Chrome Versão 39.0.2171.65 (64 bits)!
Edelans
A solução é fácil, mas infelizmente não é suportada no IE e no Safari.
Valkirilov
diz que não há permissão para o acesso
Hackbal Teamz
50

Não percorra todas as linhas de arquivo. Use readfile , é mais rápido. Isto está fora do site php: http://php.net/manual/en/function.readfile.php

$file = $_GET["file"];
if (file_exists($file)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header("Content-Type: application/force-download");
    header('Content-Disposition: attachment; filename=' . urlencode(basename($file)));
    // header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    ob_clean();
    flush();
    readfile($file);
    exit;
}

Certifique-se de higienizar sua variável get, pois alguém pode baixar alguns arquivos php ...

Alex V
fonte
4
A função readfile é realmente mais rápida. Eu, pessoalmente, recomendo usar esta resposta em vez do aceite
José Garrido
24

Em vez de usar um script PHP, para ler e liberar o arquivo, é mais interessante reescrever o cabeçalho usando .htaccess. Isso manterá um URL "legal" (em myfile.pdfvez de download.php?myfile).

<FilesMatch "\.pdf$">
ForceType applicaton/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
Rob W
fonte
Isso não faria TODOS os seus PDFs forçarem o download?
TecBrat 26/09/14
1
@TecBrat Sim, mas foi o que o OP pediu. Se você deseja limitar apenas a alguns PDFs, edite a ^\.pdf$expressão regular.
26614 Rob Rob W
1
@TecBrat ou coloque o arquivo .htaccess apenas na subpasta em que esse comportamento é necessário.
habakuk
14

Encontrei uma maneira de fazê-lo com HTML e JavaScript / jQuery simples e antigo que degrada normalmente. Testado no IE7-10, Safari, Chrome e FF:

HTML para link de download:

<p>Thanks for downloading! If your download doesn't start shortly, 
<a id="downloadLink" href="...yourpdf.pdf" target="_blank" 
type="application/octet-stream" download="yourpdf.pdf">click here</a>.</p>

jQuery (código JavaScript puro seria mais detalhado) que simula o clique no link após um pequeno atraso:

var delay = 3000;
window.setTimeout(function(){$('#downloadLink')[0].click();},delay);

Para tornar isso mais robusto, você pode adicionar a detecção de recurso HTML5 e, se não estiver lá, use window.open()para abrir uma nova janela com o arquivo.

Alex W
fonte
5

Esta é a chave:

header("Content-Type: application/octet-stream");

O aplicativo do tipo de conteúdo / x-pdf-document ou application / pdf é enviado durante o envio do arquivo PDF. O Adobe Reader geralmente define o manipulador para esse tipo de MIME, para que o navegador passe o documento para o Adobe Reader quando qualquer um dos tipos de PDF MIME for recebido.

Sudden Def
fonte
3

Eu sei que estou muito atrasado para responder a isso, mas encontrei um truque para fazer isso em javascript.

function downloadFile(src){
    var link=document.createElement('a');
    document.body.appendChild(link);
    link.href= src;
    link.download = '';
    link.click();
}
Shivek Parmar
fonte
0

Tente o seguinte:

<a href="pdf_server_with_path.php?file=pdffilename&path=http://myurl.com/mypath/">Download my eBook</a>

O código dentro do pdf_server_with_path.php é:

header("Content-Type: application/octet-stream");

$file = $_GET["file"] .".pdf";
$path = $_GET["path"];
$fullfile = $path.$file;

header("Content-Disposition: attachment; filename=" . Urlencode($file));   
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");            
header("Content-Length: " . Filesize($fullfile));
flush(); // this doesn't really matter.
$fp = fopen($fullfile, "r");
while (!feof($fp))
{
    echo fread($fp, 65536);
    flush(); // this is essential for large downloads
} 
fclose($fp);
Saill
fonte
0

Aqui está uma abordagem diferente. Prefiro confiar no suporte ao navegador ou abordá-lo na camada de aplicativos para usar a lógica do servidor da web.

Se você estiver usando o Apache e puder colocar um arquivo .htaccess no diretório relevante, poderá usar o código abaixo. Obviamente, você também pode colocar isso no httpd.conf, se tiver acesso a isso.

<FilesMatch "\.(?i:pdf)$">
  Header set Content-Disposition attachment
</FilesMatch>

A diretiva FilesMatch é apenas uma regex; portanto, ela pode ser definida da forma mais granular que você desejar, ou você pode adicionar outras extensões.

A linha Header faz a mesma coisa que a primeira linha nos scripts PHP acima. Se você precisar definir as linhas do tipo de conteúdo também, poderá fazê-lo da mesma maneira, mas não achei necessário.

Evan Donovan
fonte
0

Resolvi o meu usando todo o URL do arquivo PDF (em vez de apenas colocar o nome ou o local do arquivo em href): a href = "domain. Com / pdf / filename.pdf"

Mark Allena
fonte
0

se você precisar limitar a taxa de download, use este código !!

<?php
$local_file = 'file.zip';
$download_file = 'name.zip';

// set the download rate limit (=> 20,5 kb/s)
$download_rate = 20.5;
if(file_exists($local_file) && is_file($local_file))
{
header('Cache-control: private');
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($local_file));
header('Content-Disposition: filename='.$download_file);

flush();
$file = fopen($local_file, "r");
while(!feof($file))
{
    // send the current file part to the browser
    print fread($file, round($download_rate * 1024));
    // flush the content to the browser
    flush();
    // sleep one second
    sleep(1);
}
fclose($file);}
else {
die('Error: The file '.$local_file.' does not exist!');
}

?>

Para mais informações clique aqui

Mehran Hooshangi
fonte
0
<!DOCTYPE html>  
<html xmlns="http://www.w3.org/1999/xhtml">  
<head>  
    <title>File Uploader</title>  
    <script src="../Script/angular1.3.8.js"></script>  
    <script src="../Script/angular-route.js"></script>  
    <script src="../UserScript/MyApp.js"></script>  
    <script src="../UserScript/FileUploder.js"></script>  
    <>  
        .percent {  
            position: absolute;  
            width: 300px;  
            height: 14px;  
            z-index: 1;  
            text-align: center;  
            font-size: 0.8em;  
            color: white;  
        }  

        .progress-bar {  
            width: 300px;  
            height: 14px;  
            border-radius: 10px;  
            border: 1px solid #CCC;  
            background-image: -webkit-gradient(linear, left top, left bottom, from(#6666cc), to(#4b4b95));  
            border-image: initial;  
        }  

        .uploaded {  
            padding: 0;  
            height: 14px;  
            border-radius: 10px;  
            background-image: -webkit-gradient(linear, left top, left bottom, from(#66cc00), to(#4b9500));  
            border-image: initial;  
        }  
    </>  
</head>  
<body ng-app="MyApp" ng-controller="FileUploder">  
    <div>  
        <table ="width:100%;border:solid;">  
            <tr>  
                <td>Select File</td>  
                <td>  
                    <input type="file" ng-model-instant id="fileToUpload" onchange="angular.element(this).scope().setFiles(this)" />  
                </td>  
            </tr>  
            <tr>  
                <td>File Size</td>  
                <td>  
                    <div ng-repeat="file in files.slice(0)">  
                        <span ng-switch="file.size > 1024*1024">  
                            <span ng-switch-when="true">{{file.size / 1024 / 1024 | number:2}} MB</span>  
                            <span ng-switch-default>{{file.size / 1024 | number:2}} kB</span>  
                        </span>  
                    </div>  
                </td>  
            </tr>             
            <tr>  
                <td>  
                    File Attach Status  
                </td>  
                <td>{{AttachStatus}}</td>  
            </tr>  
            <tr>  
                <td>  
                    <input type="button" value="Upload" ng-click="fnUpload();" />  
                </td>  
                <td>  
                    <input type="button" value="DownLoad" ng-click="fnDownLoad();" />  
                </td>  
            </tr>  
        </table>  
    </div>  
</body>  
</html>   
user11021789
fonte
1
Você deve explicar o que você forneceu em seu código. Mesmo em formato reduzido, se não for possível em detalhes ou não for necessário em detalhes.
Suraj Kumar
-1

Em um aplicativo Ruby on Rails (especialmente com algo como a gema Prawn e o plug-in Prawnto Rails), você pode fazer isso um pouco mais do que um script completo (como no exemplo anterior do PHP).

No seu controlador:

def index

 respond_to do |format|
   format.html # Your HTML view
   format.pdf { render :layout => false }
 end
end

A parte render: layout => false indica ao navegador para abrir a seção "Deseja baixar este arquivo?" em vez de tentar renderizar o PDF. Em seguida, você poderá vincular o arquivo normalmente: http://mysite.com/myawesomepdf.pdf

btw
fonte
Onde escreveu este código? qual controlador, eu sou novo no PHP, por favor explique.
Prashant
@ Prashant Não é PHP, é Ruby on Rails.
22713 eloyesp
@Prashant: Se você precisar de um exemplo PHP, veja os que estão mais à frente, mas leia os comentários sobre como eles podem ser inseguros.
Evan Donovan