Come rendere il file PDF scaricabile nel collegamento HTML?


124

Sto fornendo il collegamento di un file pdf sulla mia pagina web per il download, come di seguito

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

Il problema è quando l'utente fa clic su questo collegamento, quindi

  • Se l'utente ha installato Adobe Acrobat, apre il file nella stessa finestra del browser in Adobe Reader.
  • Se Adobe Acrobat non è installato, viene visualizzato un popup all'utente per il download del file.

Ma voglio che venga sempre visualizzato all'utente per il download, indipendentemente dal fatto che "Adobe acrobat" sia installato o meno.

Per favore dimmi come posso farlo?

Risposte:


116

Invece di collegarti al file .PDF, fai invece qualcosa di simile

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

che genera un'intestazione personalizzata, apre il PDF (binario sicuro) e stampa i dati nel browser dell'utente, quindi può scegliere di salvare il PDF nonostante le impostazioni del browser. Il pdf_server.php dovrebbe assomigliare a questo:

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 ovviamente esegui alcuni controlli di integrità sulla variabile "file" per impedire alle persone di rubare i tuoi file come non accettare estensioni di file, negare barre, aggiungere .pdf al valore


2
Sto affrontando un altro problema con questo, che il mio file si trova in /products/brochure/myfile.pdf Sto dando $ file variable come $ file_path = $ _SERVER ['DOCUMENT_ROOT']. '/ Products / brochure /'. $ file; ma sta scaricando il file come "% 2Fvar% 2Fwww% 2Fweb15% 2Fweb% 2Fproducts% 2Fbrochure% 2myfile.pdf"
Prashant

3
@TravisO "Content-type: application/force-download"non è elencato da nessuna parte qui: iana.org/assignments/media-types/application È un'intestazione completamente fasulla. Si prega di non creare intestazioni e inviarle. Potresti aggiornare la tua risposta. Grazie.
Nicholas Wilson

4
Fai attenzione quando usi questo codice alla lettera, però. Ciò introduce una grave vulnerabilità LFI, poiché stai passando le variabili GET direttamente in fopen.
Joost

4
Questo codice è probabilmente pericoloso in un altro modo. Se passi i link HTTP a fopen, penso che andrà a recuperare il file da un altro server. Potrebbe essere utilizzato da un utente malintenzionato per tentare di eseguire la scansione della rete interna alla ricerca di file PDF esposti.
Rory McCune

2
Per non parlare di quanto sarebbe facile bypassare qualsiasi "controllo di integrità" che pensi di fare per il parametro "file". Gli attacchi di path injection / directory traversal sono estremamente probabili.
AVID il

210

Questo è un problema comune, ma poche persone sanno che esiste una semplice soluzione HTML 5:

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

Dove si newfilenametrova il nome file suggerito all'utente per salvare il file. Oppure verrà impostato il nome del file sul lato server se lo lasci vuoto, in questo modo:

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

Compatibilità: l'ho testato su Firefox 21 e Iron, entrambi funzionavano bene. Potrebbe non funzionare su browser non compatibili con HTML5 o obsoleti. L'unico browser che ho testato che non ha forzato il download è IE ...

Verifica la compatibilità qui: http://caniuse.com/#feat=download


9
Questa è una soluzione semplice ma sfortunatamente non molto supportata, specialmente. nessun supporto da IE caniuse.com/#feat=download
benebun

2
Sì, lo so bene. Ecco perché ho la nota a margine sulla compatibilità. E secondo la tua fonte sia IE che Safari non supportano questo approccio, o almeno non ancora :) Ad ogni modo, se vuoi che tutti i browser
eseguano il

3
funziona a meraviglia con la versione Chrome 39.0.2171.65 (64 bit)!
edelans

La soluzione è semplice ma sfortunatamente non supportata in IE e Safari.
valkirilov

dice nessun permesso di accesso
Hackbal Teamz

50

Non eseguire il ciclo in ogni riga del file. Usa invece readfile , è più veloce. Questo è fuori dal sito 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;
}

Assicurati di disinfettare la tua variabile get poiché qualcuno potrebbe scaricare alcuni file php ...


4
La funzione readfile è davvero più veloce. Personalmente consiglio di utilizzare questa risposta invece di quella accettata
Jose Garrido

24

Invece di usare uno script PHP, per leggere e scaricare il file, è più semplice riscrivere l'intestazione usando .htaccess. Ciò manterrà un URL "carino" ( myfile.pdfinvece di download.php?myfile).

<FilesMatch "\.pdf$">
ForceType applicaton/octet-stream
Header set Content-Disposition attachment
</FilesMatch>

Questo non imporrebbe il download di TUTTI i tuoi PDF?
TecBrat

1
@ TecBrat Sì, ma era quello che ha chiesto l'OP. Se desideri limitare solo alcuni PDF, modifica l' ^\.pdf$espressione regolare.
Rob W,

1
@TecBrat o inserisci il file .htaccess solo nella sottocartella in cui è necessario questo comportamento.
habakuk

14

Ho trovato un modo per farlo con il semplice vecchio HTML e JavaScript / jQuery che si degrada con grazia. Testato in IE7-10, Safari, Chrome e FF:

HTML per il link per il 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 (il codice JavaScript puro sarebbe più dettagliato) che simula il clic sul collegamento dopo un piccolo ritardo:

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

Per renderlo più robusto è possibile aggiungere il rilevamento della funzionalità HTML5 e, se non è presente, utilizzare window.open()per aprire una nuova finestra con il file.


5

Questa è la chiave:

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

Il tipo di contenuto application / x-pdf-document o application / pdf viene inviato durante l'invio del file PDF. Adobe Reader di solito imposta il gestore per questo tipo MIME in modo che il browser passi il documento ad Adobe Reader quando viene ricevuto uno qualsiasi dei tipi di PDF MIME.


3

So di essere molto in ritardo per rispondere a questa domanda, ma ho trovato un trucco per farlo in javascript.

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

0

Prova questo:

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

Il codice all'interno di 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);

0

Ecco un approccio diverso. Preferisco piuttosto che fare affidamento sul supporto del browser, o affrontarlo a livello dell'applicazione, per utilizzare la logica del server web.

Se stai usando Apache e puoi mettere un file .htaccess nella directory pertinente puoi usare il codice qui sotto. Ovviamente, potresti metterlo anche in httpd.conf, se hai accesso a quello.

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

La direttiva FilesMatch è solo una regex, quindi potrebbe essere impostata nel modo più granulare che desideri o potresti aggiungere altre estensioni.

La riga di intestazione fa la stessa cosa della prima riga negli script PHP sopra. Se è necessario impostare anche le righe del tipo di contenuto, è possibile farlo allo stesso modo, ma non l'ho trovato necessario.


0

Ho risolto il mio utilizzando l'intero URL del file PDF (invece di mettere semplicemente il nome del file o la posizione in href): a href = "domain. Com / pdf / filename.pdf"


0

se hai bisogno di limitare la velocità di download, usa questo codice !!

<?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!');
}

?>

Per maggiori informazioni clicca qui


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>   

1
Dovresti spiegare cosa hai fornito nel tuo codice. Anche in forma breve se non possibile nei dettagli o non richiesto nei dettagli.
Suraj Kumar

-1

In un'applicazione Ruby on Rails (specialmente con qualcosa come Prawn gem e il plugin Prawnto Rails), puoi farlo un po 'più semplicemente di uno script completo (come il precedente esempio PHP).

Nel tuo controller:

def index

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

La parte render: layout => false dice al browser di aprire "Vuoi scaricare questo file?" prompt invece di tentare di eseguire il rendering del PDF. Quindi sarai in grado di collegarti al file normalmente: http://mysite.com/myawesomepdf.pdf


Dove scrivere questo codice? quale controller, sono nuovo in PHP per favore spiegami.
Prashant

@Prashant Questo non è PHP, è Ruby on Rails.
eloyesp

@Prashant: Se hai bisogno di un esempio PHP, guarda quelli più in alto nel thread, ma leggi i commenti su come potrebbero essere insicuri.
Evan Donovan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.